分布式平台搭建

搭建Hadoop

以下操作皆基于Ubuntu 16.04 32位环境 & Hadoop 2.7.3版本

单节点环境

  • 首先安装相关的准备环境

    1
    2
    3
    4
    $ sudo apt update
    $ sudo apt install -y default-jdk
    $ sudo apt install -y vim
    $ sudo apt install -y openssh-server
  • 配置JAVA_HOME

    • 一般来说Ubuntu平台上通过default-jdk安装的java位置在/usr/lib/jvm/default-jdk目录
    • /etc/profile中添加下边两行内容并保存退出
      1
      2
      export JAVA_HOME=/usr/lib/jvm/default-java
      export PATH=$PATH:$JAVA_HOME/bin
  • 添加用户和用户组

    1
    2
    3
    $ sudo addgroup hadoop
    $ sudo adduser --ingroup hadoop hduser
    $ sudo usermod -a -G sudo hduser
  • 配置 SSH

    • 切换到hduser,并执行以下操作

      1
      2
      $ ssh-keygen -t rsa -P ""
      $ cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
    • 之后尝试使用下边的命令连接本机,这个操作也会将本机加入到known hosts里

      1
      $ ssh localhost
  • 安装Hadoop

    • 下载hadoop-2.7.3.tar.gz,以下假定工作目录为/home/hduser

      1
      2
      3
      $ tar -zxvf hadoop-2.7.3.tar.gz
      $ mv hadoop-2.7.3/ hadoop/
      $ sudo chown -R hduser:hadoop hadoop
  • 编辑bash配置

    • 打开.bashrc,加入以下内容(也可以修改/etc/profile,但是使用.bashrc更加灵活)

      1
      2
      3
      export JAVA_HOME=/usr/lib/jvm/default-java
      export HADOOP_HOME=/home/hduser/hadoop
      export PATH=$PATH:$JAVA_HOME/bin:$HADOOP_HOME/bin
  • 配置Hadoop(不同版本的hadoop的配置方案会有不同,配置之前记得要确定版本)

    • 修改~/hadoop/etc/hadoop/hadoop-env.sh,在文件中添加下边的内容

      1
      export JAVA_HOME=/usr/lib/jvm/default-java
    • 修改~/hadoop/etc/hadoop/yarn-env.sh,在文件中添加下边的内容

      1
      export JAVA_HOME=/usr/lib/jvm/default-java
    • 修改~/hadoop/etc/hadoop/core-site.xml

      • 执行以下命令

        1
        2
        3
        $ sudo mkdir -p /home/hduser/tmp
        $ sudo chown hduser:hadoop /home/hduser/tmp
        $ sudo chmod 750 /home/hduser/tmp
      • 在core-site.xml添加以下内容(各个属性的内容可以按照实际情况修改)

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        <configuration>
        <property>
        <name>fs.defaultFS</name>
        <value>hdfs://master:9000</value>
        </property>
        <property>
        <name>io.file.buffer.size</name>
        <value>131072</value>
        </property>
        <property>
        <name>hadoop.tmp.dir</name>
        <value>file:/home/hduser/tmp</value>
        <description>Abase for other temporary directories.</description>
        </property>
        <property>
        <name>hadoop.proxyuser.hduser.hosts</name>
        <value>*</value>
        </property>
        <property>
        <name>hadoop.proxyuser.hduser.groups</name>
        <value>*</value>
        </property>
        </configuration>
    • 修改~/hadoop/etc/hadoop/hdfs-site.xml,添加以下内容(各个属性的内容可以按照实际情况修改)

      • 执行以下命令

        1
        2
        3
        4
        5
        6
        $ sudo mkdir -p /home/hduser/dfs/name
        $ sudo chown hduser:hadoop /home/hduser/dfs/name
        $ sudo chmod 750 /home/hduser/dfs/name
        $ sudo mkdir -p /home/hduser/dfs/data
        $ sudo chown hduser:hadoop /home/hduser/dfs/data
        $ sudo chmod 750 /home/hduser/dfs/data
      • 在hdfs-site.xml中添加以下内容

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        <configuration>
        <property>
        <name>dfs.namenode.secondary.http-address</name>
        <value>master:9001</value>
        </property>
        <property>
        <name>dfs.namenode.name.dir</name>
        <value>file:/home/hduser/dfs/name</value>
        </property>
        <property>
        <name>dfs.datanode.data.dir</name>
        <value>file:/home/hduser/dfs/data</value>
        </property>
        <property>
        <name>dfs.replication</name>
        <value>3</value>
        </property>
        <property>
        <name>dfs.webhdfs.enabled</name>
        <value>true</value>
        </property>
        </configuration>
    • 修改~/hadoop/etc/hadoop/mapred-site.xml,添加以下内容(各个属性的内容可以按照实际情况修改)

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      <configuration>
      <property>
      <name>mapreduce.framework.name</name>
      <value>yarn</value>
      </property>
      <property>
      <name>mapreduce.jobhistory.address</name>
      <value>master:10020</value>
      </property>
      <property>
      <name>mapreduce.jobhistory.webapp.address</name>
      <value>master:19888</value>
      </property>
      </configuration>
    • 修改~/hadoop/etc/hadoop/yarn-site.xml(各个属性的内容可以按照实际情况修改)

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      <configuration>
      <property>
      <name>yarn.nodemanager.aux-services</name>
      <value>mapreduce_shuffle</value>
      </property>
      <property>
      <name>yarn.nodemanager.aux-services.mapreduce.shuffle.class</name>
      <value>org.apache.hadoop.mapred.ShuffleHandler</value>
      </property>
      <property>
      <name>yarn.resourcemanager.address</name>
      <value> master:8032</value>
      </property>
      <property>
      <name>yarn.resourcemanager.scheduler.address</name>
      <value> master:8030</value>
      </property>
      <property>
      <name>yarn.resourcemanager.resource-tracker.address</name>
      <value> master:8031</value>
      </property>
      <property>
      <name>yarn.resourcemanager.admin.address</name>
      <value> master:8033</value>
      </property>
      <property>
      <name>yarn.resourcemanager.webapp.address</name>
      <value> master:8088</value>
      </property>
      </configuration>
  • 格式化Namenode

    • 进入~/hadoop/bin目录下,执行以下操作

      1
      $ ./hdfs namenode –format
  • 启动hadoop

    • ~/hadoop/sbin执行以下命令

      1
      2
      $ ./start-dfs.sh
      $ ./start-yarn.sh
    • 之后通过jps命令可以看到以下结果,说明启动成功

      1
      2
      3
      4
      13058	Jps
      13026 NodeManager
      12916 ResourceManager
      12169 DataNode

集群环境

此处默认master和slave已经按照单机节点配置完成

  • 设置网络

    • 我的例子是一台master一台slave,分别确定两者的ip,然后在两台机器的/etc/hosts中写入以下内容

      1
      2
      192.168.0.1    master
      192.168.0.2 slave
  • 配置ssh免密登录

    • 在master上执行以下命令

      1
      $ ssh-copy-id -i ~/.ssh/id_rsa.pub hduser@slave
    • 在slave上执行以下命令

      1
      $ ssh-copy-id -i ~/.ssh/id_rsa.pub hduser@master
    • 之后两方互相登录测试一下是否可以免密登录

  • 配置~/hadoop/etc/hadoop/slaves

    • 在master机器的slaves文件中添加以下内容

      1
      2
      master
      slave
  • 格式化Namenode

    • 进入master的~/hadoop/bin目录下,执行以下操作

      1
      $ ./hdfs namenode –format
  • 启动hadoop

    • 在master的~/hadoop/sbin执行以下命令

      1
      2
      $ ./start-dfs.sh
      $ ./start-yarn.sh
    • 之后通过jps命令可以看到以下结果,说明启动成功

      • master中

        1
        2
        3
        4
        5
        6
        4048	Jps
        3899 ResourceManager
        4013 NodeManager
        3726 SecondaryNameNode
        3423 NameNode
        3535 DataNode
      • slave中

        1
        2
        3
        2754	DataNode
        3012 Jps
        2903 NodeManager

搭建Spark

Spark平台

  • 下载Spark

    • 这里下载和hadoop版本对应的spark,假定工作目录为/home/hduser

    • 进行如下操作解压压缩包(每个节点都要进行)

      1
      $ tar -zxvf spark-2.3.1-bin-hadoop2.7.tgz
  • 配置~/.bashrc文件

    • 在.bashrc文件中添加以下内容(每个节点都要添加)

      1
      2
      export SPARK_HOME=/home/hduser/spark
      export PATH=$PATH:$SPARK_HOME/bin
  • 配置~/spark/conf/slaves文件

    • 在两个节点的salves文件中添加以下内容

      1
      2
      master
      slave
  • 启动Spark

    • 在master的~/spark/sbin中执行以下命令

      1
      ./start-all.sh
    • 在master中执行jps,发现以下结果说明spark运行成功

      1
      2
      3
      4
      5
      6
      5257	Worker
      5177 Master
      3726 SecondaryNameNode
      5326 Jps
      3432 NameNode
      3535 DataNode
    • 在slave中执行jsp,发现一下结果说明spark运行成功

      1
      2
      3
      2754	DataNode
      3717 Worker
      3771 Jps

配置Scala

  • 下载Scala

    • 这里相应版本的scala压缩包,假定工作目录为/home/hduser

    • 进行如下操作解压压缩包(只需要在master进行)

      1
      $ tar -zxvf scala-2.12.6.tgz
  • 配置~/.bashrc文件

    • 在.bashrc文件中添加以下内容(只需要在master在master上执行以下命令)

      1
      2
      export SCALA_HOME=/home/hduser/scala
      export PATH=$PATH:$SCALA_HOME/bin

配置 Eclipse

  • 安装Eclipse

    • 在master上执行以下命令

      1
      $ sudo apt install eclipse-platform
  • 安装Eclipse插件

参考资料

  1. 在Ubuntu上搭建单节点Hadoop
  2. 在Ubuntu上搭建Hadoop集群

茶和牛奶

墙头雨细垂纤草,水面风回聚落花。

井放辘轳闲浸酒,笼开鹦鹉报煎茶。

床上躺了一下午,起身换衣服后准备去吃晚饭,转念一想又懒得去了,于是便用热牛奶冲了点玉米片应付一下,或许是饿了,冲好之后直接喝了一大口,被狠狠烫了一下。冒冒失失吃完之后,躺在椅子上端起一杯茶小口抿着,心里突然想到:“为何自己喝茶时能这么慢条斯理,但是喝牛奶时却么狼狈”,想来想去觉得是“无欲无求”四个字。在喝牛奶时是冲着其明确的价值去的,因此并不是为了喝牛奶而喝牛奶,于是喝的过程被简化和忽略了,进而缺少了一种仪式感,而仪式感带来的就是从容和优雅。

无怪乎古人爱饮茶。爱茶之人爱的不仅是茶的香沁,更爱的是饮茶时的淡然。今人面临着越来越快的世界和不断膨胀的欲望,很难实时保有无欲无求的心境,但人心总是会累的,因此偶尔忘掉世俗烦心事是非常重要的一种能力。无论生活如何,偷浮生半日,捧一杯香茗,小口品味,钟摆骤然慢了下来,心情也变得开阔和舒缓,于是轻轻放下茶杯,欣慰地夸上一句“真是好茶”,实乃人生一大快事。

繁事压身,聊作酸文。

狼曋和羊斟

狼曋

在崤之战中,晋军全歼秦军,晋襄公抓住了一名秦国小卒,命车右莱驹将其斩杀。主将居车左,护卫居车右,身为车右的莱驹本应是晋襄公最看重的人,然而此时那秦卒突然怪叫,吓得莱驹手中的戈掉在了地上,自己也跌落车下。

狼曋此时只是晋军的一个无名小卒,这时候在旁边看到了这一幕,快步上前捡起戈一下刺死了秦卒,拎小鸡似的一把抓起莱驹追上了晋襄公的战车。晋襄公认为狼曋有过人之勇,便罢黜了莱驹,提拔狼曋为车右。

好景不长,一段时间之后,主帅先轸(这是一位非常了不起的人物,也是第一位被称为元帅的人)认为狼曋只凭借一时之勇当上车右,而没有真正护卫国君的能力,因此又罢免了他(一说先轸之后免胄殉翟时怕狼曋来救,因此暂时罢免他,但是已将其考虑为接班人)。

狼曋从高峰跌到谷底非常生气,怒发冲冠,他的朋友鲜伯看热闹不嫌事大,说道:“你既然这么生气,那么敢去以死明志吗?”。狼曋说死谁不敢,就是没有一个好的理由。鲜伯说那还不容易,我们一起去把先轸杀了。这时候狼曋说道:“勇而害上,不登于明堂,死而不义,非勇也!”,鲜伯自愧不如。

秦将孟明视所带领的秦军在崤之战中全军覆没,为报仇雪恨,三年以后,孟明视带大军与晋军交战于彭衙,这就是著名的彭衙之战。在这一战中,双方还未正式交战,斜地里便有一支几百人的军队不怕死地冲入秦军阵营,打得秦军措手不及,因而大乱,晋军趁机大败秦军,孟明视再次狼狈而归。这支几百人的军队就是狼曋所带领,狼曋也在这次战争中阵亡,他一直休养生息等待机会来证明自己的勇,并且用行动诠释了“能为国死,方为勇”。

羊斟

羊斟,宋国主帅华元的车右。在狼曋死后几十年,郑国攻打宋国。出战之前,华元请部下吃羊肉来鼓舞士气,然而偏偏漏掉了自己的车右羊斟。这小心眼的羊斟记恨在心,找机会寻仇。宋郑两军交战以后,羊斟对华元说:“吃羊肉的事你说了算,不过今天这事我说了算”(“畴昔之羊,子为政;今日之事,我为政”,这也是成语“各自为政”的出处),说着便把车子往郑国大军里赶。可怜华元堂堂主帅就这样不明不白地被活捉。

这场战争的失败给宋国造成了极大的损失,不仅损失了一员大将和几百兵士,还损失了数百辆战车,之后宋国为了把华元从郑国赎出,又花了大笔钱财。这件事在史书上只有寥寥数笔,但是我们不难推测,作为华元车右的羊斟是华元多么信任的人,可后者就为了区区一碗羊肉干出了这样丧心病狂的事情,史书上对羊斟的评价是“羊斟非人也,以其私憾,败国殄民,于是刑孰大焉?”。

同为车右,羊斟和狼曋是截然不同的两个典型,不能不让人唏嘘。

春秋霸主晋文公

公子重耳

春秋时期的晋国,晋献公有三个儿子:公子申生,公子重耳和公子夷吾。献公五年(公元前672年),晋献公攻打骊戎,掳回来骊姬和她妹妹当妃子,二女分别为献公生下公子奚齐和卓子。要说这晋献公也不全是昏庸无能,假道伐虢的事就是晋献公干的,除此之外,晋献公在政治和军事上也做了很多利国利民的好事。但是晋献公后来受到骊姬挑拨,先是逼死了太子申生,又逼走了公子重耳和夷吾,之后晋国就开始了十数年的动乱。

晋献公先是派大内第一高手寺人披去杀公子重耳,重耳无奈出走,开始了十九年的流亡生涯,逃亡生涯过了一半时,献公死了,公子夷吾即位。重耳心想回去吧,但是晋国有着优良的窝里斗传统,再加上夷吾知道重耳比自己更适合国君的位置,所以又派寺人披去杀重耳。重耳深知寺人披这死太监没有职业操守,说五更杀你其实三更就摸到你床沿了,因此穿着睡衣拖鞋就上路继续逃亡了。

逃亡中的重耳哪个国家没有去过?狄国、卫国、齐国、曹国、宋国、郑国、楚国、秦国都溜了一遍,可以说是身经百战了。其中有的国家对他很好,比如齐国、楚国、秦国,重耳在楚国时,楚成王问他以后做了国君要怎么回报自己,重耳说:“如果将来两国不幸打仗,那么我将退避三舍”。秦国更是亲手将重耳扶上了国君之位。有的国家只当他是个落魄公子,不正眼相待,比如曹国、郑国,这曹国国君甚至偷看重耳洗澡,就差朝地上扔肥皂了。郑国国君也不拿重耳当回事,大夫叔瞻劝告郑文公如果不能善待重耳,那就杀了他免留后患,这话被重耳知道了,记在了心上。

十九年间,有屈辱下的鸿鹄之志,也有款待下的纸醉金迷,最终在秦穆公的护送下,年逾花甲的公子重耳才回到了晋国,是为晋文公。

城濮之战

公子重耳回到晋国之后,进位为晋文公。晋文公虽然年事已高,但是成就霸业之心不灭,晋国在晋文公的治理下已有了霸主之实,但还没有霸主之名,所以晋文公一直在寻找一个合适的机会称霸。要什么来什么,公元前632年,楚国进攻宋国,宋国急忙向盟国晋国求救,晋文公认为如果打败了楚国,那么登上霸主之位便是顺理成章。然而晋国后方有秦齐两个强大的国家,晋文公不敢大举动兵腾空国力,于是便想确定秦齐的意向,正巧齐国和秦国都喜受贿赂,于是晋国让宋国给秦齐送去大量的财宝,依次将它们拉拢到同一条战线。

秦齐两国在收了宋国的好处之后非常高兴,在外交上对楚国施压,逼楚国退兵。晋文公心想这楚国还不能退兵,仗一旦打不起来就没法成就霸主了,这时候大将先轸给晋文公出了主意:去攻打楚国的附属国曹、卫两国,这样一来楚国必然不会甘心退兵。晋文公照办,灭掉了曹、卫两国,楚国大将子玉咽不下这口气,不听楚成王退兵的命令,执意带兵攻打晋国。晋文公遵守当年对楚王退避三舍的约定,连退九十里,其实遵守约定是其一,这么做更多原因是骄兵战术,诱敌深入。子玉在晋文公退避三舍后仍然穷追不舍,于是双方在城濮交战。此战晋军大败楚军,楚军退兵途中子玉自杀,由此晋文公一战而中原服。

城濮之战结束之后,晋文公把周天子襄王请到了践土,并献上战利品,周天子命晋文公为诸侯之长,有征讨天下诸侯的权力,从此晋文公正式坐上了霸主的位置,成为春秋时期继齐桓公之后的第二位中原霸主。实际上从这里也能看出周王室衰败成什么样子,堂堂周天子被一个诸侯呼来唤去,毫无王权可言,因此《春秋》中为了避讳这件事,将周天子出现在这里的原因写为“周王巡狩河阳”。

烛武退秦

春秋中后期,晋文公在秦穆公的支持下坐上了国君的位置,在城濮之战中,晋国狠挫楚国锐气,当上了春秋霸主。晋文公不是个心胸宽广的人,坐稳位置之后就想起当年逃亡在外时在郑国受的委屈,准备报仇。僖公三十年(约公元前630年),晋文公叫上秦穆公一起进军郑国,并许诺均分郑国土地。晋文公首先要求郑国交出叔瞻,因为他在当年重耳流亡时曾劝说郑文公杀掉重耳,叔瞻无奈自杀。叔瞻死后,晋文公又要求交出郑国国君,郑国朝内大乱,这个时候郑国大夫佚之狐向郑文公推荐了烛之武,说这个时候只有他能救郑国。郑文公把烛之武叫来,烛之武早年没有得到重用,这会儿国君有求于他,自然要发发牢骚,所以说道:“我年轻的时候还不如别人,现在老了就更不行了”,郑文公苦笑着赔罪哄了半天,烛之武才同意去游说秦国。当天晚上烛之武就吊着威亚从城门跑下去,天亮溜到秦军大营,找到了秦穆公。

烛之武见到秦穆公,从秦穆公的角度说此次助晋攻打郑国的坏处,说的天花乱坠口水横飞。其实虽然很多人对于《烛之武退秦师》的评价是“层次分明、组织严密、说理透彻、逻辑严密”云云,然而其实烛之武的论点并不是都经得起推敲:“许君焦、瑕,朝济而夕设版焉,君之所知也”,然而其实这并不是晋文公干的事,完全的张冠李戴强行抹黑,“既东封郑,又欲肆其西封,若不阙秦,将焉取之”,然而晋国不一定非得向西扩张,西边是自己的盟国,向东攻齐,向南攻楚,向北攻中山、燕国都可以,没有必要非得先拿自己的盟友下手。秦穆公何许人也?在位数十年的老狐狸了,眼睫毛都是空的,怎么可能被烛之武这几句话给糊弄过去,然而有一句话烛之武说对了,帮助晋国打郑国对于秦国并没有好处。秦国地处西戎,要想在中原发展势力,总是有晋国的阻碍,这时候打下了郑国,只能是增加自己日后扩张的阻力——晋国虽然北南东三个方向都能扩张,然而秦国想要扩张,只有往东一条路可以走。因此秦穆公思索再三,决定退兵。

秦穆公虽然是退兵了,但是心里还是打着小算盘,想要独吞郑国这块大肥肉,因此我们看到《烛之武退秦师》里的“秦伯说,与郑人盟。使杞子、逢孙、杨孙戍之,乃还”,杞子、逢孙、杨孙这三员大将可不是去义务站岗的,而是驻扎在那里等待攻下郑国的时机。秦国军队一撤,晋国也只得撤军,两国的梁子也从这里结下了。

崤山之战

彭衙之战

  • 狼曋:
  • 羊斟:

王官之战


参考资料

  1. 《烛之武退秦师》
  2. 百家讲坛·秦晋大战
  3. 百家讲坛·秦晋相持
  4. 百度百科·晋献公
  5. 百度百科·重耳流亡
  6. 百度百科·城濮之战
  7. 互动百科·城濮之战

Python 'python ImportError: No module named XXX' 问题

最近做毕业设计,用Python写一个文本爬虫,我的目录结构是这样的:

1
2
3
4
5
6
7
StockTextDigger
----src
--------main.py
--------__init__.py
----parser
--------__init__.py
--------source_parser.py

当要从main.py中引用source_parser.py中的一个类时出现了“python ImportError: No module named source_parser”的错误。之前写过的Python从没出现过这样的问题,可是这次无论责骂么都解决不聊,无论是加上 __init__.py 还是sys.path.append(‘XXX’)都没有用,然后忽然意识到parser这个包很有可能已经在默认搜索路径中了,然后改名字,顺利解决。

又一个官逼同的典型例子[摊手]

关于新旧交替的历史进程

最近心里一直很闷。

最早要从我关注hanser说起,一年前关注了hanser,之后取关,一个多月前又关注了回来。然后对她喜欢的一发不可收拾,去翻遍了她的微博,然后了解到了鬼叔——神威鬼鸣。

看过2012年B站的拜年祭,鬼叔相当于那个时期的B站元老,无限风光,是创世神一般的存在。就在前几天,鬼叔在微博上发布了长微博,说是自己生活压力其实非常大,九月份已经确诊了抑郁症,之后不会再在B站活动。(2021年8月最新更新:鬼叔的B站账号已经被封,理由大概是古早时期的《千本幼女》)

仅仅四年的时间,一个时代已经过去了,从hanser的关注者开始,我用人肉深搜扒出了一大堆上古时期的B站大神,已知最早的是09年到13年一直活跃的河童子,MAD周刊排行榜创始人。对于他来说,鬼叔都只是个后辈。这批元老现在都几乎不在B站活动了(当然可能是充实的生活让他们脱离了这个精神寄托,这里我们就事论事),有些还是因为和B站创始人徐逸发生了矛盾被打压而后离开的。有些离开的人在优酷投稿播放量是B站十倍以上。他们是真的爱这个地方。

看着这一切,这其实没什么大不了的一切,我却生出了一种深深的历史空洞感。一个时代过去了,人心一致的辉煌时代过去了,仅仅是几年的时间。那么放眼这数千年的历史,类似大大小小的历史进程已经上演了无数次,每次一定都让相关的人大发感慨,然而现在的我们连一朵水花都看不到了。

正当我感慨生不逢时之时,我忽然明白了原因:我怀念B站的上古时代,就像我上边提到的诸位大神怀念A站一样,都是在无条件地怀念过去,他们或许看着A站曾经辉煌的痕迹也曾发出过与我类似的感叹。

看看的如今的B站,真的那么不堪吗?不是的,厉害的新人越来越多,高质量的投稿也越来越多—虽然低质量的更多—大家也都找到了适合自己的商业模式,努力向前。我在怀念过去时选择性无视了这一点而已。“这是最好的时代,也是最坏的时代”,我多少能理解一点这句话的意思了,或许意思不对,就当我断章取义了吧。

我现在多少能体会为什么孔子他老人家一直推崇周礼,也能理解为什么现在的人动不动就说人心不古我当年如何如何云云。都是对过去的怀念和对现在的选择性无视罢了。时代确实在一直变得更好,即使有很多需要着力矫正的畸形的地方,也不会阻挡历史的趋势。

把握当下。这是一句轻飘飘的话,高中作文娴熟地运用了无数次,而个中真意非亲身经历不能理解。我不敢说自己理解了,但是要好过从前。对过去的怀念有时源于没有掌控住现在,现在的我就是这种情况,只要把握住了现在,那么未来就可以感慨现在的我是生得恰逢其时了。

很惊讶我竟然能说出这种高浓度鸡汤。我一直很讨厌这种动动嘴皮子就把一切客观条件和主观意愿都视作无物的心灵毒药。然而在经历了一些事情之后,自己的心境确实会发生变化,这的确是成长的过程。

就当作是复习之余的矫情牢骚吧。

待我回家,带我回家,代我回家

待我回家,带我回家,代我回家

十年了,小哥说我如果没有忘,再回到这里还能见到他。如果小哥会从长白山出来,那墨脱的喇嘛每隔十年接待的客人又是谁呢?

我抽了一口烟,他妈的全都是胖子的汗臭味,比直接嚼烟叶还提神。“我说胖爷,小花就真的不下来看看吗?”,我看了看远处火光中时隐时现的青铜门,眼里闪过一丝黯然。胖子躺在碎石上,一边抠脚一边念叨,“只有真正的离开,才能真正的结束,真丫添堵,难道胖爷要交待在这?早知道我就好好当我的村支书了,你来见你的小情人,胖爷我来凑什么热闹啊”。

鬼玺跟小花在上边,不知道这青铜门到时间会不会自己打开,我身上穿着小哥留在外边的衣服,心说一开门看到小哥光着屁股在地上找衣服那乐子可就大了。可是如果看到小哥变成四阿公那样的僵尸也不是不可能,四阿公年纪大了,我还能用石头压住他,从鼻腔拿出钥匙,万一小哥变成僵尸,估计我还来不及把钥匙塞到鼻孔就被他扭断脖子了。我甩了甩头,强迫自己不去想这些东西。

正当我胡思乱想的时候,一边的胖子突然咋呼起来,“你他娘的敢揩胖爷的油!胖爷我砸死你个老不修!”,只见胖子一手拿着石头和一团什么东西扭打在一起,胖子一半是气的一半是吓的,手起石落砸的一下比一下狠,“哎呦卧槽你他娘还有尾……真他娘的邪性!天真你看看这个”,胖子说着把那“尾巴”揪下来扔了过来,然后就神色古怪地转过了头不再看我,我定睛一看只觉脑子轰的一声。胖子扔过来的是一把古金刀,这刀我只在一个人身上见过。我赶紧过去,原来胖子砸的是来时路上的阴兵,我凑上去看了下脸,虽然紫的发黑,眼睛也是一片死白,但是这脸怎么看怎么像……怎么看怎么像,小哥。

我坐在地上,脑子里嗡嗡直响,一片空白,就在这时,远处火光中的青铜门轰隆隆地响了起来……tbc

写在前面

因为一些原因,现在暂时停止了Android的学习,转向JavaWeb的学习。说是这样,其实我不论哪一样都没有真的学得透彻,顶多算是个入门。因为现在仍然处于迷茫期,不知道在研究生阶段结束后(目前大三)到底直接出去搬砖还是再去国外深造。但不管怎么说,毕竟专业相关,这些东西还是掌握的越多越好。

JavaWeb 之前是接触过一点的,不过也限于做一个大作业网站的程度。现在开始从零开始一点点地做,把过程记录在这里,用作日后的备忘。

这个专题准备一直写下去,不过一年之内可能做不了太多东西,因为现在得忙考研的事情。现在开始后悔前几年没好好学习净捣鼓编程上的东西了,并不是后悔方向错了,而是后悔即使这样也没做出什么东西来,所以还是有一点虚度光阴的感觉。

各种各样的框架、设计模式层出不穷,其实搞得我挺烦的。明明就是某个场景下的某个解决方案而已,但是总是想搞得老子天下第一(技术无罪,框架开发者无罪,但是某些框架的某些不明事理的“狂热支持者”着实烦人)。当然,我现在是看源码都费劲,水平确实非常次。所以准备拿住一个框架开始琢磨,准备从 spring 开始,把 Data JPA、MVC、Security 什么的都先了解了再说,然后再决定下一步的路。

说实话确实挺迷茫的,这样下去自己根本没有竞争力,只是这样是绝对不够的,不过还是得从这里起步,至于是不是涉足安全、智能或者VR的领域,等研究生阶段再做定论。

软工大作业·历物语(二)

文章来源:中国软工亚洲指挥中心

共同作者:纪神,爵爷,老板,小男孩(按首字拼音排序)

责任编辑:爵爷

进度汇总

先大致说一下这两周完成的内容:

  • 登录界面
  • 注册界面
  • 新闻详情界面
  • 用户偏好算法(初版)的设计与实现
  • 通知系统
  • 私信系统
  • 部分实现了数据操作辅助类族
  • 部分实现了推送系统
  • 部分UI的优化

细节还有非常非常多,当然不是说要拘泥于细节而失去了整体意识,而是在细节的把控中了解整个项目的脉络。虽然功能远没有实现完,但是现在的项目结构已经略显臃肿了,所以下一周准备梳理一下整个项目,开始尝试自上而下地审视并重新设计一下整个结构。

本周做的工作值得细说的还是用户偏好算法的设计。用户偏好算法是我们项目的核心算法,目的是为推测出用户近期对于新闻类型的偏好,并记录在系统内,从而在之后可以让用户选择自己感兴趣的信息,并且每当发布了用户感兴趣的信息之后可以推送给用户。除此之外,最重要的一点是,系统可以根据用户的喜好向用户提供兴趣相投的好友,从而提升用户黏性,并可以隐形中推广应用。

作为一个用户偏好推测算法,可以划分到人工智能的领域,在这里我想谈一下对于算法起点的考虑。说到机器学习,普通水平的实现只要看了Coursera上吴恩达的Machine Learning就够用了(当然我是说非常简单的应用),再不然Github上一堆数千star的项目,甚至都不需要知道基本概念。但是在初版算法设计中我们并没有采用机器学习算法,这是由于以下几点考虑:

  • 我们采集的数据特征值非常少
  • 我们采集的数据量非常少
  • 手机计算资源的限制
  • 在对机器学习没有深入的研究的情况下,设计出的算法有时还不如最普通的迭代

在初版算法设计中,我们只是使用了简单的权值计算,目前先实现这版测试一下效果如何,具体设计如下:

算法目的

为推测出用户近期对于新闻类型的偏好,并记录在系统内,从而在之后可以让用户选择自己感兴趣的信息,并且每当发布了用户感兴趣的信息之后可以推送给用户。除此之外,最重要的一点是,系统可以根据用户的喜好向用户提供兴趣相投的好友,从而提升用户黏性,并可以隐形中推广应用。

算法原理

算法计算结果是用户对于某种类型的新闻的偏好程度,目的在于两部分,第一部分是推测近期趋势,第二部分是推测用户喜好。

为了体现这两部分,我们使用两个衡量指标:时间因子 $tf$ (time factor)和认真程度 $cd$ (conscientious degree)来表示。为了突出两方面的因素,我们使用高阶运算来提升权值,计算出的值为 $tpw$ (temporary preference weight)。
除此之外,由于偏好值的计算是不断迭代的,所以每次计算新的偏好值时应该考虑之前的计算结果,给定一个权值因子 $wf$ (weight factor),并设旧的用户偏好值为opw(old preference weight),则新的用户偏好值为 $npw = opw \times wf + (1 – wf) \times tpw$, 目前取 $wf$ 为0.3,并取 $opw$ 初值为0。

算法过程

把用户访问一篇新闻的时间以及在这篇新闻上的停留时间分别设为 $visitTime$ (单位:毫秒)和 $stayTime$ (单位:毫秒),考虑通过这两个因素来计算 $tf$ 和 $cd$。

  1. 对于 $tf$ 的计算,主要是体现用户的偏好有多“新”,考虑取看这篇新闻距离现在的时间间隔,即 $currentTime – visitTime$, 换算成天数设为 $dayInterval$, 那么可以取 $tf = \frac{30}{dayInterval + 1}$ (取30为了使 $tf$ 和 $cd$ 处于同一个数量级)
  2. 对于 $cd$ 的计算,取平均阅读每个字所用的时间,设新闻正文字数为 $newsLength$ ,则 $cd = \frac{stayTime}{newsLength}$
  3. 对于 $tpw$ 的计算,为了使越新、阅读越认真的新闻重要性越大且增长速度越来越快,可以取 $tf$ 和 $cd$ 和的平方,即 $tpw = (tf + cd) ^ 2$。而用户在某一种类的新闻浏览记录有 $n$ 条,设每条记录计算的 $tpw$ 值为 $tpwi$,综上所述可得 $tpwi = (\frac{30 \times 24 \times 3600 \times 1000}{currentTime – visitTime + 86400000} + \frac{stayTime}{newsLength}) ^ 2$
  4. 则 $tpw$ 最终计算结果为。最终计算所得的 $npw = wf \times opw + (1 – wf) \times tpw$。(注意:还可以考虑的一点是,如果我是信息学院计算机系的,你也是信息学院计算机系的,那么我们很可能就有共同语言,也就是同一学院、同一学系或者同一年级的用户也可能有共同的喜好,即使这一点没有体现在算法计算结果上,考虑把这一点也加在偏好算法计算中)

算法实施

我们在数据库中设置了一张用户偏好表用于记录用户偏好信息,表项有用户 $id$、新闻类型、偏好旧值和详细信息,在详细信息中记录了一个数组,数组中每一个元素都包含 $visitTime$ 和 $stayTime$ 两部分。

每当用户访问一篇新闻时,都会更新相应的表项。同时在用户表中添加一个喜好标签表项,记录用户最近最感兴趣的新闻类型,最多记录三个,太多的话匹配结果会过多,使得匹配系统没有了意义,而且用户也没有精力去关注那么多的新闻类型。当分别计算完一个用户对于各个新闻类型的喜好权值之后,将其排序,得到的前三名新闻类型放入喜好标签中。

当推送新闻时,直接根据标签过滤即可。推荐好友时,比对两人的喜好标签即可,只要有共同的标签就推荐,并且如果当前用户没有喜好标签,那么就随机从所有用户中抽取;如果当前用户有喜好标签,那么对于其他待匹配的没有喜好标签的用户而言,也采用随机抽取的办法。

关于用户喜好的计算时机,因为计算不是在服务器上完成的(用云引擎有点麻烦而且也没必要占用服务器资源),所以考虑只要用户登录了,就在后台计算用户偏好,每个登录周期偏好只计算一次。

目前为止只做了简单的试验,算法可以得到较为合理的偏好值,但是在真实的应用场景中效果如何还有待进一步的测试。