吴哥日出

IMG_5623

看吴哥日出,比较累的一点就是要很早起来,然后记得当时就头晕了,带错了镜头,本来应该带广角的,结果却带了长焦,于是怎么都拍不出吴哥的雄伟。

临离开的时候,一回头,地下一个小水洼,景致倒是不错,水面的反光也恰好五颜六色。吴哥窟的塔群在水里,影影绰绰。

这几年,看世界,知道很多事情,不是非黑即白。小水洼都可以容纳这么美的景致,我们岂可辜负造物主。

忙碌的炎夏

这一个月,又是忙忙碌碌,一般来说半年前后和年底前总是最忙的,各类业绩充指标,各类项目告一段落,开始下一个里程碑。

没想到,大数据和机器学习发展的这么快,虽然不懂的东西很多,但是赶上了这个时间点,还是很高兴,公司这两年缓了过来,飞速的发展,真是不容易。

减肥第一阶段基本成功,瘦了十斤,不过同样有一个月左右徘徊不前了,天气又很热,走不动,也不太愿意锻炼,哈哈。

以前觉得这样忙忙碌碌有点不知道目的地在哪里,所谓瞎忙,现在感觉,每个人的努力虽然有限,但是合在一起还是巨大的能量,真的可以改变一些东西。甚至改变世界。

 

学习 python 这一年

大约2015年4月到5月开始,正式学习 python,发现之前大概在2009年左右也学过,可能当时没有坚持下去。

记得去年开始学 python 的时候,1个月左右,给同事写了一个基于 csv 处理的小程序,当时就被 python 的简洁优雅所倾倒。

然后边学边写东西,本来为公司 zion 框架写一个 dsl,当时水平还不行,所以写成了一个 sql 的包装工具 r2,用来在前端业务逻辑调用后台数据库的时候简化 sql 语句写法以及做到和数据库无关。

真正让我自己 python 水平感觉有点突破的是几个事情:

1 写真正生产上的应用。需要考虑的事情非常多,比如一个简单的 r2,就让我学到了 log、conf、import、class 等很多基本知识的应用,以及单元测试、性能测试。

2 教别人 python。这是让自己的基础知识巩固最好的办法之一。要把别人教会,且处理各类奇怪的运行错误,很有挑战。这样的结果,是对诸如列表、字符串、元组、字典、函数、循环等基本概念非常清楚。不管多复杂的程序,90%的代码其实还是基本的操作,在这90%的代码设计和编写的时候可以减少错误,节约时间,自然有用。

3 看资料。晚上有大量的英文和中文的 python 资料,绝大多数都写的很好,受益匪浅。

在我的推进下,公司也在个别项目上开始使用 python,通过 flask 来构建 restful 接口,机器学习也基于 keras 和 tensorflow 开始了实际的应用。

我相信,进步会是跳跃式的,而这还是仅仅发生在一年间,python 在胶水语言的应用,连接 js、php、java 和 moble 开发上面,以及 python 在人工智能、机器学习上这几年强劲的表现,都让人激动不已。

记得当年在豪斯登堡乐园

2012年第一次去日本旅游,是跟团的半自由行,当时对日本的无知现在想想好笑,比如在熊本没有拍一张熊本熊的照片,因为根本不知道这个是吉祥物。

同样,第一天到佐贺之后,去的是豪斯登堡乐园,根本不知道有那么美,那么大。按照我现在的度假策略,那个地方是特别适合休息的。并且在这个乐园里的酒店,非常大,不像一般日本的酒店那样比较小比较精致。

整理老照片时候,想起那年,还是拍了不少很赞的照片的,加上现在后期修正技术也提高了一些。

豪斯登堡乐园

春去秋来 自然花开

休假的时候,面向大海,瞎想想,规划一下之后的人生。

过了四十岁,很多事情不如以前那么去争了,大部分(可惜还做不到)事情也都看的淡一些。

IMG_7999

争也罢,老板也罢,财富也罢,都不如内心强大来的重要。

与其高调炫耀,不如低调自己。

面向大海 心态平和

三十多岁的时候,也有很多创业的梦想,后来诸多变故,于是最近这些年心态平和了很多。

世界这么大,自然要多看看,也喜欢拍照,都有自己解集出版的想法了。

其实,我是不反对创业的,对于新事物的学习我也特别有精神。不过,看到很多年轻人其实并不是创业心态,而是活在别人的看法里,或者想着一夜致富,千万富翁。社会大环境使然,浮躁了。

假期小憩,在苏梅岛,安静的休息,面朝大海,春暖花开。

IMG_7358

IMG_8034

每个人都是自己的生活的主人,以前不太懂,过于在乎别人怎么看自己,其实这样很累,自己开心最重要,再说,绝大多数事情也不是想要得到就能得到,谋事在人,成事在天。

所有在努力工作、创业,为了自己梦想而打拼的朋友,都不容易,别在意别人怎么看,别去和别人比,一切皆自然。

每次旅行,可以安静的想想,沉淀自己,偶尔做个哲学家,也不错。

我的2016 Python B 班

从三月开始,江湖救急。

也一直好为人师。

终于找到了舞台。

昨天,这13个小孩,Python 初级班结束。

谢谢豆妈,谢谢静枫,谢谢所有的家长,谢谢聪明的孩子们。

很多时候感觉回到了自己小时候,在少科站、少年宫、格致老大楼的机房。

IMG_7951

IMG_7952

或许,这样耗费很多业余时间,来回路上,准备讲义和题目,制作考试用的 rpg 游戏,有的人会觉得我很傻,很多时候我也认为自己不是一个一流的程序员,但是当这些孩子们编程入门了,或许明天的 facebook、google 的创造者就在他们中间,想想这也是很开心的。

贵阳之行

两天,来回飞机都延误,加起来延误了六个小时,或许好事多磨。

IMG_4186

印象中是第一次去贵州,第一次去贵阳,也是第一次去一个省政府谈事情,天气很好。

贵州这几年变成了中国的大数据基地,发展很快。但城市或许是因为高速建设的缘故,堵车很厉害,回来的时候听司机说了一路的抱怨,包括物价。其实恐怕这不单单是贵州贵阳才有的问题,上海何尝不是。

匆匆之行,闻到茅台的香,可惜我不喝酒。

难得的星期天

90年代中后期开始,全国开始五天工作+两天休息了。

这之前,有过五天半,当时很麻烦,其实是换休的,就是一周休息五天,下一周休息六天,当时在读大学,课程表和什么时候回家经常有点小糊涂。

最近两年,也是忙忙碌碌,因为时间安排的缘故,所以周六周日总是有事,命运的安排绝大多数时候都不受自己控制。

今天周日,难得不用跑出去,本来准备在家里做一些学习研究,昨天下午缺胃开始疼了起来,晚上早早睡了。想起今天这个可以略微放松的星期天,一种说不去的惬意。

性格的确是决定命运的,在一些关键时间点,人生就会发生改变。

性格也非常难改变,大概只能微调。

已经过了追求过多目标的年龄,除了一些自己喜欢的东西,还是有点执着以外,培养部下也是不遗余力,其他的,都随遇而安了,也不是很在乎别人怎么看。

窗外春雨绵绵。有的人看出惆怅,有的人看出江南。

使用 python 基于朴素贝叶斯进行文本分类学习笔记之五:增加停用词

还记得之前我们搭建的朴素贝叶斯的分类模型,在正面负面各9个样本的情况下,正确率如下。

{'01': 1, '10': 5, '00': 7, '11': 3}
(0.875, 0.375)

 

我开始引入停用词,从测试来看,主要是在训练的时候效果比较明显,因为中文样本中标点符号和很多常用词占比还是比较大的,引入停用词机制前后,差不多是三分之一的量。

下面是读入停用词的函数代码。

 def read_stopwords(self):

        self.stopwords_list = []

        # 获得停用词文件的本地文件
        filename = get_long_filename_with_sub_dir_module('bayes', 'stopwords.txt')[1]

        with open(filename, 'r') as f:
            for line in f:
                self.stopwords_list.append(line.rstrip())

        print(self.stopwords_list)

 

目前我是将朴素贝叶斯的这个分类,作为 fish_base 包的一部分功能。对于停用词来说属于功能部分,而之前的训练文档其实不是功能部分,是由真实代码或者 demo 代码传递给分类器的。因此训练文档和测试文档属于 demo 代码,而停用词属于分类器代码。

(国内网上很多的贝叶斯代码的解释者可能都是学习数据分析、统计分析的职业人士,但是代码的写法和扩展性,略显简单,我可能习惯了尽量做得完善一些,副作用是会比较复杂一点,需要更多 python 和编程的基础)

整个类的代码最后给出。这里解释一下

get_long_filename_with_sub_dir_module

这个函数是为了获得模块运行时候的路径,否则一般的获取路径办法获得是执行文件的路径,这就是之前说的如果只是一个 demo,不会有这个问题,统统在一个路径下面,但是如果考虑做成包,做成 module,封装逻辑的话,就会复杂一点,另外还要考虑各个操作系统可能的差异。

这个函数属于 fish_base 的 commmon 部分,目前 fish_base 公开的版本1.0.7 中还不支持,在 github 上的1.0.8 支持该函数。

停用词来自于之前说到过的 snownlp 包,感谢作者。停用词数量大约1400个左右,以后考虑支持用户自定义停用词。

在训练过程引入停用词之后,再用昨天的测试样本测试一下,得到了如下的结果。

{'00': 8, '10': 4, '01': 0, '11': 4}
(1.0, 0.5)

 

正面被分类成正面准确率100%,负面被分类成负面也从37.5% 提升到 50%,略有进步。

按照公司数据分析团队的建议,后者百分比还不够高是因为训练样本太少的缘故。

如此少的训练样本,得到了这样的结果,不得不说朴素贝叶斯的确很厉害,一点也不 naive。

之后我们还会继续改进的地方是:
1 将判断错误的样本增加到训练样本中,来重新训练
2 分词,增加用户自定义词汇
3 测试材料,增加停用词处理(目前测试下来,对提升正确率没有作用),这也和目前举例中的测试文本比较短有关,并且这个也和性能有关,训练过程中引入停用词判断对性能的损失是无所谓的,反正是在训练过程中,但是实际使用分类还是要越快越好
4 我们会将整个分类器通过 Falsk 包装成 web 接口,加入 job 概念,这样调用程序只需要传入训练文本和测试文本,之后就可以生成特定的分类器概率矩阵,然后供调用程序在真实场景下使用,作为调用程序的项目组不需要知道后面这些细节
5 所有的数据存储可以本地实例化,sqlite 或者 mysql 之类的支持
6 支持三元和四元的分类

从用 python 来实现朴素贝叶斯分类器来对中文文本进行倾向性分析来说,暂时告一段落了。所有代码都是这几天里面改写和增加的,防止错误的部分几乎没有,也没有进行完整的单元测试。

下面是当前版本完整的朴素贝叶斯分类器的类的代码,直接使用需要有 python 基础,也可以之后使用 fish_base 包来调用。

最新版本源代码在这里: github.com/chinapnr/fish_base

pypi 的安装包会稍微滞后一些。

from numpy import *
import jieba

from fish_base import get_long_filename_with_sub_dir_module


class ClassNaiveBayes:

    # 训练 list, 默认内容, 原书中的内容
    train_doc_list = [['my', 'dog', 'has', 'flea', 'problems', 'help', 'please'],
                      ['maybe', 'not', 'take', 'him', 'to', 'dog', 'park', 'stupid'],
                      ['my', 'dalmation', 'is', 'so', 'cute', 'I', 'love', 'him'],
                      ['stop', 'posting', 'stupid', 'worthless', 'garbage'],
                      ['mr', 'licks', 'ate', 'my', 'steak', 'how', 'to', 'stop', 'him'],
                      ['quit', 'buying', 'worthless', 'dog', 'food', 'stupid']]
    # 倾向性向量, 0:正面 1:负面
    train_doc_sent_vec = [0, 1, 0, 1, 0, 1]

    # 单词列表集合
    word_list = []

    # 正面和负面概率, 先验概率
    p0_v = 0
    p1_v = 0
    p_ab = 0

    stopwords_list = []

    # 2016.5.18
    # 读入停用词
    def read_stopwords(self):

        self.stopwords_list = []

        # 获得停用词文件的本地文件
        filename = get_long_filename_with_sub_dir_module('bayes', 'stopwords.txt')[1]

        with open(filename, 'r') as f:
            for line in f:
                self.stopwords_list.append(line.rstrip())

        print(self.stopwords_list)

    # 2016.5.19
    def word_optimize(self, l):
        # 停用词过滤
        temp_word_list = [x for x in l if x not in self.stopwords_list]
        return temp_word_list

    # 2016.5.16 5.19
    # 创建单词集合
    # 输入 data_list: 数据列表内容, 两维list
    # 输出 单维list
    def create_word_list(self, data_list):
        # create empty set
        word_set = set([])
        for document in data_list:
            # union of the two sets
            word_set = word_set | set(document)
        word_list = list(word_set)
        # 词汇处理
        word_list = self.word_optimize(word_list)
        # print('word list count:', len(word_list))
        return word_list

    # 2016.5.16
    # 将单词 list 转换为向量
    # 输入 word_list: 单词列表 new_word_list: 需要向量化的单词列表
    # 输出 vec: 生成的向量数组
    @staticmethod
    def words_to_vec(word_list, new_word_list):
        vec = [0] * len(word_list)
        for word in new_word_list:
            if word in word_list:
                vec[word_list.index(word)] += 1
            else:
                pass
                # print("the word: %s is not in my Vocabulary!" % word)
        return vec

    # 2016.5.16
    # 进行 naive bayes 训练
    # 输入 train_matrix: 训练举证 train_category: 正反向量列表
    # 输出 p0_v, p1_v:正面反面概率, p_ab:先验概率
    @staticmethod
    def train_nb0(train_matrix, train_category):

        num_train_docs = len(train_matrix)
        num_words = len(train_matrix[0])
        p_ab = sum(train_category) / float(num_train_docs)

        # 创建给定长度的填满1的数组
        p0_num = ones(num_words)
        p1_num = ones(num_words)

        p0_d = 2.0
        p1_d = 2.0
        for i in range(num_train_docs):
            if train_category[i] == 1:
                p1_num += train_matrix[i]
                p1_d += sum(train_matrix[i])
            else:
                p0_num += train_matrix[i]
                p0_d += sum(train_matrix[i])

        p1_v = log(p1_num / p1_d)
        p0_v = log(p0_num / p0_d)
        return p0_v, p1_v, p_ab

    # 2016.5.16
    # 分类
    # 输入 向量, 正面反面概率,事件概率
    # 输出 正面或者反面
    @staticmethod
    def classify_nb(vec, p0_vec, p1_vec, p_class1):
        # element-wise mult
        p0 = sum(vec * p0_vec) + log(1.0 - p_class1)
        p1 = sum(vec * p1_vec) + log(p_class1)
        print('p0:', p0, 'p1:', p1)
        if p1 > p0:
            return 1
        else:
            return 0

    # 2016.5.16
    # 训练, 生成需要的向量参数等
    def train(self):
        # 生成单词列表集合
        self.word_list = self.create_word_list(self.train_doc_list)

        # 训练矩阵初始化
        train_matrix = []

        # 根据训练文档进行循环
        for post_in_doc in self.train_doc_list:
            # 构建训练矩阵, 将单词列表转化为向量
            train_matrix.append(self.words_to_vec(self.word_list, post_in_doc))

        # 根据训练矩阵和情感分析向量进行训练,得到
        self.p0_v, self.p1_v, self.p_ab = self.train_nb0(array(train_matrix), array(self.train_doc_sent_vec))

    # 2016.5.16
    # 根据输入内容测试 Naive Bayes 模型
    # 输入 test_word_list: 需要测试的单词 list
    # 输出 0 or 1, 表示正面或者反面
    def run_nb(self, word_list):

        # 对输入的内容转化为向量
        this_post_vec = array(self.words_to_vec(self.word_list, word_list))

        # 返回分类的值
        return self.classify_nb(this_post_vec, self.p0_v, self.p1_v, self.p_ab)

    # 2016.5.18
    @staticmethod
    def init_cut_word():
        jieba.initialize()

    # 2016.5.18
    # 打开训练文档, 将内容增加到内部变量
    def open_train_doc_ch(self, filename, class_mark):

        train_txt0 = []
        with open(filename, 'r') as f:
            for line in f:
                train_txt0.append(line.rstrip())

        for item in train_txt0:
            s = list(jieba.cut(item))
            self.train_doc_list.append(s)
            self.train_doc_sent_vec.append(class_mark)

    # 2016.5.18
    # 根据测试样本来测试分类的准确率
    # 输出 人工正确/机器判断正确 人工错误/机器判断错误 的两个百分比
    def test_nb(self, filename):

        test_doc_list = []
        pre_class_list = []

        # 设定测试结果 dict
        test_result_dict = {'11': 0, '10': 0, '00': 0, '01': 0}

        # 打开测试文本
        with open(filename, 'r') as f:
            for line in f:
                # 获得人工设定的类别
                pre_class_list.append(line[0:1])
                # 获得需要测试的文本
                test_doc_list.append(line[2:].rstrip())

        # 测试文本的长度
        test_doc_count = len(test_doc_list)

        for i, item in enumerate(test_doc_list):

            s = list(jieba.cut(item))

            # 对输入单词进行优化处理
            s = self.word_optimize(s)

            # 获得程序分类结果
            computer_class = self.run_nb(s)
            # 获得人工设定的分类结果
            pre_class = pre_class_list[i]

            # 结果记录到测试结果 dict 中
            index = '**'

            if pre_class == '1':
                index = str(10 + computer_class)
            if pre_class == '0':
                index = '0' + str(computer_class)

            test_result_dict[index] += 1

            print(s, pre_class, computer_class)

        print(test_result_dict)

        # 返回结果, 假设测试文本中正面和负面各占一半
        return test_result_dict['00'] / (test_doc_count / 2), test_result_dict['11'] / (test_doc_count / 2)

 

下面是测试用的程序。

from fish_base import bayes
import jieba

nb = bayes.ClassNaiveBayes()

nb.train()

test_list = ['love', 'my', 'dalmation']
print(test_list, 'classified as: ', nb.run_nb(test_list))

test_list = ['stupid', 'garbage']
print(test_list, 'classified as: ', nb.run_nb(test_list))

nb.train_doc_list = []
nb.train_doc_sent_vec = []

# for Chinese

# 初始化分词
nb.init_cut_word()

# 读入停用词
nb.read_stopwords()

nb.open_train_doc_ch('train_bayes/good.txt', 0)
nb.open_train_doc_ch('train_bayes/bad.txt', 1)

nb.train()
print(nb.p0_v, nb.p1_v, nb.p_ab)

print(nb.test_nb('train_bayes/test.txt'))

# 指定测试
print()
test_s = '这个手机很好,我很喜欢'
print(test_s)
test_list = list(jieba.cut(test_s))
p = nb.run_nb(test_list)
if p == 0:
    print('classified as good ')
else:
    print('classified as bad ')

# while 1:
#     test_s = input('input comment:')
#     test_list = list(jieba.cut(test_s))
#     print(test_list)
#     p = nb.run_nb(test_list)
#     if p == 0:
#         print('classified as good ')
#     else:
#         print('classified as bad ')
#
#     print()

# 2016.5.18
# {'01': 1, '10': 5, '00': 7, '11': 3}
# (0.875, 0.375)
#
#
# 2016.5.18 增加去除停用词,一般意义
# {'11': 4, '00': 8, '10': 4, '01': 0}
# (1.0, 0.5)

 

(感谢这几天项目开发团队和数据分析开发团队同事支持和耐心讲解。)

使用 python 基于朴素贝叶斯进行文本分类学习笔记之一:开始
使用 python 基于朴素贝叶斯进行文本分类学习笔记之二:将原书程序修改并转换为类
使用 python 基于朴素贝叶斯进行文本分类学习笔记之三:中文内容的倾向性判断初步
使用 python 基于朴素贝叶斯进行文本分类学习笔记之四:增加测试文本和计算正确率
使用 python 基于朴素贝叶斯进行文本分类学习笔记之五:增加停用词