评论进行分类

        由于处理的是字符串,我们首先要想方法把字符串转换为向量/数字表示。一种解决方法是可以把单词映射为数字ID。

        第二个问题是每行评论字数不同,而神经网络需要一致的输入(其实有些神经网络不需要,至少本帖需要),这可以使用词汇表解决。

        代码部分:

        安装nltk(自然语言工具库 Natural Language Toolkit

         pip install nltk

        下载nltk数据:

         控制台输入 Python,然后输入以下命令。
        >>> import nltk
        >>> nltk.download()

        等待其安装完成;测试

           >>> from nltk.corpus import brown
           >>> brown.words()
           ['The', 'Fulton', 'County', 'Grand', 'Jury', 'said', ...]

        检测代码:

        

[python]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. #coding=utf-8  
  2. import numpy as np  
  3. import tensorflow as tf  
  4. import random  
  5. import cPickle  
  6. from collections import Counter  
  7.   
  8. import nltk  
  9. from nltk.tokenize import word_tokenize  
  10.   
  11.   
  12. """ 
  13. 'I'm super man' 
  14. tokenize: 
  15. ['I', ''m', 'super','man' ] 
  16. """  
  17. from nltk.stem import WordNetLemmatizer  
  18.   
  19. """ 
  20. 词形还原(lemmatizer),即把一个任何形式的英语单词还原到一般形式,与词根还原不同(stemmer),后者是抽取一个单词的词根。 
  21. """  
  22.   
  23. pos_file = 'pos.txt'  
  24. neg_file = 'neg.txt'  
  25.   
  26.   
  27. # 创建词汇表  
  28. def create_lexicon(pos_file, neg_file):  
  29.     lex = []  
  30.   
  31.     # 读取文件  
  32.     def process_file(f):  
  33.         with open(pos_file, 'rb') as f:  
  34.             lex = []  
  35.             lines = f.readlines()  
  36.             # print(lines)  
  37.             for line in lines:  
  38.                 words = word_tokenize(line.lower())  
  39.                 lex += words  
  40.             return lex  
  41.   
  42.     lex += process_file(pos_file)  
  43.     lex += process_file(neg_file)  
  44.     # print(len(lex))  
  45.     lemmatizer = WordNetLemmatizer()  
  46.     lex = [lemmatizer.lemmatize(word) for word in lex]  # 词形还原 (cats->cat)  
  47.   
  48.     word_count = Counter(lex)  
  49.     # print(word_count)  
  50.     # {'.': 13944, ',': 10536, 'the': 10120, 'a': 9444, 'and': 7108, 'of': 6624, 'it': 4748, 'to': 3940......}  
  51.     # 去掉一些常用词,像the,a and等等,和一些不常用词; 这些词对判断一个评论是正面还是负面没有做任何贡献  
  52.     lex = []  
  53.     for word in word_count:  
  54.         if word_count[word] < 2000 and word_count[word] > 20:  # 这写死了,好像能用百分比  
  55.             lex.append(word)  # 齐普夫定律-使用Python验证文本的Zipf分布 http://blog.topspeedsnail.com/archives/9546  
  56.     return lex  
  57.   
  58.   
  59. lex = create_lexicon(pos_file, neg_file)  
  60.   
  61.   
  62. # lex里保存了文本中出现过的单词。  
  63.   
  64. # 把每条评论转换为向量, 转换原理:  
  65. # 假设lex为['woman', 'great', 'feel', 'actually', 'looking', 'latest', 'seen', 'is'] 当然实际上要大的多  
  66. # 评论'i think this movie is great' 转换为 [0,1,0,0,0,0,0,1], 把评论中出现的字在lex中标记,出现过的标记为1,其余标记为0  
  67. def normalize_dataset(lex):  
  68.     dataset = []  
  69.   
  70.     # lex:词汇表;review:评论;clf:评论对应的分类,[0,1]代表负面评论 [1,0]代表正面评论  
  71.     def string_to_vector(lex, review, clf):  
  72.         words = word_tokenize(line.lower())  
  73.         lemmatizer = WordNetLemmatizer()  
  74.         words = [lemmatizer.lemmatize(word) for word in words]  
  75.   
  76.         features = np.zeros(len(lex))  
  77.         for word in words:  
  78.             if word in lex:  
  79.                 features[lex.index(word)] = 1  # 一个句子中某个词可能出现两次,可以用+=1,其实区别不大  
  80.         return [features, clf]  
  81.   
  82.     with open(pos_file, 'r') as f:  
  83.         lines = f.readlines()  
  84.         for line in lines:  
  85.             one_sample = string_to_vector(lex, line, [10])  # [array([ 0.,  1.,  0., ...,  0.,  0.,  0.]), [1,0]]  
  86.             dataset.append(one_sample)  
  87.     with open(neg_file, 'r') as f:  
  88.         lines = f.readlines()  
  89.         for line in lines:  
  90.             one_sample = string_to_vector(lex, line, [01])  # [array([ 0.,  0.,  0., ...,  0.,  0.,  0.]), [0,1]]]  
  91.             dataset.append(one_sample)  
  92.   
  93.     # print(len(dataset))  
  94.     return dataset  
  95.   
  96.   
  97. dataset = normalize_dataset(lex)  
  98. random.shuffle(dataset)  
  99. """ 
  100. #把整理好的数据保存到文件,方便使用。到此完成了数据的整理工作 
  101. with open('save.cPickle', 'wb') as f: 
  102.     cPickle.dump(dataset, f) 
  103. """  
  104.   
  105. # 取样本中的10%做为测试数据  
  106. test_size = int(len(dataset) * 0.1)  
  107.   
  108. dataset = np.array(dataset)  
  109.   
  110. train_dataset = dataset[:-test_size]  
  111. test_dataset = dataset[-test_size:]  
  112.   
  113. # Feed-Forward Neural Network  
  114. # 定义每个层有多少'神经元''  
  115. n_input_layer = len(lex)  # 输入层  
  116.   
  117. n_layer_1 = 1000  # hide layer  
  118. n_layer_2 = 1000  # hide layer(隐藏层)听着很神秘,其实就是除输入输出层外的中间层  
  119.   
  120. n_output_layer = 2  # 输出层  
  121.   
  122.   
  123. # 定义待训练的神经网络  
  124. def neural_network(data):  
  125.     # 定义第一层"神经元"的权重和biases  
  126.     layer_1_w_b = {'w_': tf.Variable(tf.random_normal([n_input_layer, n_layer_1])),  
  127.                    'b_': tf.Variable(tf.random_normal([n_layer_1]))}  
  128.     # 定义第二层"神经元"的权重和biases  
  129.     layer_2_w_b = {'w_': tf.Variable(tf.random_normal([n_layer_1, n_layer_2])),  
  130.                    'b_': tf.Variable(tf.random_normal([n_layer_2]))}  
  131.     # 定义输出层"神经元"的权重和biases  
  132.     layer_output_w_b = {'w_': tf.Variable(tf.random_normal([n_layer_2, n_output_layer])),  
  133.                         'b_': tf.Variable(tf.random_normal([n_output_layer]))}  
  134.   
  135.     # w·x+b  
  136.     layer_1 = tf.add(tf.matmul(data, layer_1_w_b['w_']), layer_1_w_b['b_'])  
  137.     layer_1 = tf.nn.relu(layer_1)  # 激活函数  
  138.     layer_2 = tf.add(tf.matmul(layer_1, layer_2_w_b['w_']), layer_2_w_b['b_'])  
  139.     layer_2 = tf.nn.relu(layer_2)  # 激活函数  
  140.     layer_output = tf.add(tf.matmul(layer_2, layer_output_w_b['w_']), layer_output_w_b['b_'])  
  141.   
  142.     return layer_output  
  143.   
  144.   
  145. # 每次使用50条数据进行训练  
  146. batch_size = 50  
  147.   
  148. X = tf.placeholder('float', [None, len(train_dataset[0][0])])  
  149. # [None, len(train_x)]代表数据数据的高和宽(矩阵),好处是如果数据不符合宽高,tensorflow会报错,不指定也可以。  
  150. Y = tf.placeholder('float')  
  151.   
  152.   
  153. # 使用数据训练神经网络  
  154. def train_neural_network(X, Y):  
  155.     predict = neural_network(X)#得到预测结果(通过神经网络)  
  156.     cost_func = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(predict, Y))#定义损失函数  
  157.     optimizer = tf.train.AdamOptimizer().minimize(cost_func)  # learning rate 默认 0.001  
  158.   
  159.     epochs = 13#13次整体迭代  
  160.     with tf.Session() as session:  
  161.         session.run(tf.initialize_all_variables())  
  162.         epoch_loss = 0  
  163.   
  164.         i = 0  
  165.         random.shuffle(train_dataset)#数据集随机  
  166.         train_x = dataset[:, 0]#train_x  
  167.         train_y = dataset[:, 1]#标签  
  168.         #训练过程  
  169.         for epoch in range(epochs):  
  170.             while i < len(train_x):  
  171.                 start = i  
  172.                 end = i + batch_size#训练了一个batch大小  
  173.   
  174.                 batch_x = train_x[start:end]  
  175.                 batch_y = train_y[start:end]  
  176.   
  177.                 _, c = session.run([optimizer, cost_func], feed_dict={X: list(batch_x), Y: list(batch_y)})  
  178.                 epoch_loss += c#一个epoch内一个batch的损失  
  179.                 i += batch_size#下一个开始位置  
  180.   
  181.             print(epoch, ' : ', epoch_loss)#输出  所有数据后的一次迭代  
  182.   
  183.         text_x = test_dataset[:, 0]#测试数据  
  184.         text_y = test_dataset[:, 1]#标签  
  185.         correct = tf.equal(tf.argmax(predict, 1), tf.argmax(Y, 1))  
  186.         accuracy = tf.reduce_mean(tf.cast(correct, 'float'))  
  187.         print('准确率: ', accuracy.eval({X: list(text_x), Y: list(text_y)}))  
  188.   
  189.   
  190. train_neural_network(X, Y)  

执行结果:

这个结果,说明效率并不高,当然你每次运行时结果是不一样的,但最高也就70%+。

那么问题出在哪呢?

准确率低主要是因为数据量太小,同样的模型,如果使用超大数据训练,准确率会有显著的提升。

下文我会使用同样的模型,但是数据量要比本文使用的多得多,看看准确率能提高多少。由于本文使用的神经网络模型(feed-forward)过于简单,使用大数据也不一定有质的提升,尤其是涉及到自然语言处理。


很多自然语言处理(NLP)的任务,包括分词、词性标注、命名实体识别(NER)及句法分析。

 一 nltk安装教程

     首先,保证已经安装成功python。然后终端输入命令:pip install nltk;安装完成后,输入import nltk了,然后输入nltk.download(),这样就可以打开一个NLTK Downloader(NLTK下载器)。(具体安装过程:http://www.pythontip.com/blog/post/10011/)

     成功安装后,测试。输入下边的语句就可以:

     >>> from nltk.corpus import brown
     >>> brown.words()
     ['The', 'Fulton', 'County', 'Grand', 'Jury', 'said', ...]
 
二   NLTK进行分词

使用的函数:

nltk.sent_tokenize(text) #对文本按照句子进行分割

nltk.word_tokenize(sent) #对句子进行分词

三 NLTK进行词性标注

用到的函数:

nltk.pos_tag(tokens)#tokens是句子分词后的结果,同样是句子级的标注


四 NLTK进行命名实体识别(NER)

用到的函数:

nltk.ne_chunk(tags)#tags是句子词性标注后的结果,同样是句子级


上例中,有两个命名实体,一个是Xi,这个应该是PER,被错误识别为GPE了; 另一个事China,被正确识别为GPE。

五 句法分析

nltk没有好的parser,推荐使用stanfordparser
但是nltk有很好的树类,该类用list实现
可以利用stanfordparser的输出构建一棵python的句法树


六 词干提取(stemming)

      解释一下,Stemming 是抽取词的词干或词根形式(不一定能够表达完整语义)。NLTK中提供了三种最常用的词干提取器接口,即 Porter stemmer, Lancaster Stemmer 和 Snowball Stemmer。

Porter Stemmer基于Porter词干提取算法,来看例子: 

[python]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. >>> from nltk.stem.porter import PorterStemmer    
  2. >>> porter_stemmer = PorterStemmer()    
  3. >>> porter_stemmer.stem(‘maximum’)    
  4. u’maximum’    
  5. >>> porter_stemmer.stem(‘presumably’)    
  6. u’presum’    
  7. >>> porter_stemmer.stem(‘multiply’)    
  8. u’multipli’    
  9. >>> porter_stemmer.stem(‘provision’)    
  10. u’provis’    
  11. >>> porter_stemmer.stem(‘owed’)    
  12. u’owe’    

Lancaster Stemmer 基于Lancaster 词干提取算法,来看例子


[python]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. >>> from nltk.stem.lancaster import LancasterStemmer    
  2. >>> lancaster_stemmer = LancasterStemmer()    
  3. >>> lancaster_stemmer.stem(‘maximum’)    
  4. ‘maxim’    
  5. >>> lancaster_stemmer.stem(‘presumably’)    
  6. ‘presum’    
  7. >>> lancaster_stemmer.stem(‘presumably’)    
  8. ‘presum’    
  9. >>> lancaster_stemmer.stem(‘multiply’)    
  10. ‘multiply’    
  11. >>> lancaster_stemmer.stem(‘provision’)    
  12. u’provid’    
  13. >>> lancaster_stemmer.stem(‘owed’)    
  14. ‘ow’    

Snowball Stemmer基于Snowball 词干提取算法,来看例子

[python]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. >>> from nltk.stem import SnowballStemmer    
  2. >>> snowball_stemmer = SnowballStemmer(“english”)    
  3. >>> snowball_stemmer.stem(‘maximum’)    
  4. u’maximum’    
  5. >>> snowball_stemmer.stem(‘presumably’)    
  6. u’presum’    
  7. >>> snowball_stemmer.stem(‘multiply’)    
  8. u’multipli’    
  9. >>> snowball_stemmer.stem(‘provision’)    
  10. u’provis’    
  11. >>> snowball_stemmer.stem(‘owed’)    
  12. u’owe’    
七 词形还原(lemmatization)
        Lemmatisation是把一个任何形式的语言词汇还原为一般形式(能表达完整语义)。相对而言,词干提取是简单的轻量级的词形归并方式,最后获得的结果为词干,并不一定具有实际意义。词形还原处理相对复杂,获得结果为词的原形,能够承载一定意义,与词干提取相比,更具有研究和应用价值。

       我们会在后面给出一个同MaxMatch算法相结合的更为复杂的例子。


八  最大匹配算法(MaxMatch)

        MaxMatch算法在中文自然语言处理中常常用来进行分词(或许从名字上你已经能想到它是基于贪婪策略设计的一种算法)。通常,英语中一句话里的各个词汇之间通过空格来分割,这是非常straightforward的,但是中文却没有这个遍历。例如“我爱中华人民共和国”,这句话被分词的结果可能是这样的{‘我’,‘爱’,‘中华’,‘人民’,‘共和国’},又或者是{‘我’,‘爱’,‘中华人民共和国’},显然我们更倾向于后者的分词结果。因为‘中华人民共和国’显然是一个专有名词(把这样一个词分割来看显然并不明智)。我们选择后者的策略就是所谓的MaxMatch,即最大匹配。因为‘中华人民共和国’这个词显然要比‘中华’,‘人民’,‘共和国’这些词都长。


        我们可以通过一个英文的例子来演示MaxMatch算法(其实中文处理的道理也是一样的)。算法从右侧开始逐渐减少字符串长度,以此求得可能匹配的最大长度的字符串。考虑到我们所获得的词汇可能包含有某种词型的变化,所以其中使用了Lemmatisation,然后在词库里进行匹配查找。

[python]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. from nltk.stem import WordNetLemmatizer    
  2. from nltk.corpus import words    
  3.     
  4. wordlist = set(words.words())    
  5. wordnet_lemmatizer = WordNetLemmatizer()    
  6.     
  7. def max_match(text):    
  8.     pos2 = len(text)    
  9.     result = ''    
  10.     while len(text) > 0:           
  11.         word = wordnet_lemmatizer.lemmatize(text[0:pos2])    
  12.         if word in wordlist:    
  13.             result = result + text[0:pos2] + ' '    
  14.             text = text[pos2:]    
  15.             pos2 = len(text)    
  16.         else:    
  17.             pos2 = pos2-1                   
  18.     return result[0:-1]    

来看看算法的实现效果

   

[python]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. >>> string = 'theyarebirds'    
  2. >>> print(max_match(string))    
  3. they are birds 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值