由于处理的是字符串,我们首先要想方法把字符串转换为向量/数字表示。一种解决方法是可以把单词映射为数字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', ...]
检测代码:
-
- import numpy as np
- import tensorflow as tf
- import random
- import cPickle
- from collections import Counter
-
- import nltk
- from nltk.tokenize import word_tokenize
-
-
-
-
-
-
-
- from nltk.stem import WordNetLemmatizer
-
-
-
-
-
- pos_file = 'pos.txt'
- neg_file = 'neg.txt'
-
-
-
- def create_lexicon(pos_file, neg_file):
- lex = []
-
-
- def process_file(f):
- with open(pos_file, 'rb') as f:
- lex = []
- lines = f.readlines()
-
- for line in lines:
- words = word_tokenize(line.lower())
- lex += words
- return lex
-
- lex += process_file(pos_file)
- lex += process_file(neg_file)
-
- lemmatizer = WordNetLemmatizer()
- lex = [lemmatizer.lemmatize(word) for word in lex]
-
- word_count = Counter(lex)
-
-
-
- lex = []
- for word in word_count:
- if word_count[word] < 2000 and word_count[word] > 20:
- lex.append(word)
- return lex
-
-
- lex = create_lexicon(pos_file, neg_file)
-
-
-
-
-
-
-
- def normalize_dataset(lex):
- dataset = []
-
-
- def string_to_vector(lex, review, clf):
- words = word_tokenize(line.lower())
- lemmatizer = WordNetLemmatizer()
- words = [lemmatizer.lemmatize(word) for word in words]
-
- features = np.zeros(len(lex))
- for word in words:
- if word in lex:
- features[lex.index(word)] = 1
- return [features, clf]
-
- with open(pos_file, 'r') as f:
- lines = f.readlines()
- for line in lines:
- one_sample = string_to_vector(lex, line, [1, 0])
- dataset.append(one_sample)
- with open(neg_file, 'r') as f:
- lines = f.readlines()
- for line in lines:
- one_sample = string_to_vector(lex, line, [0, 1])
- dataset.append(one_sample)
-
-
- return dataset
-
-
- dataset = normalize_dataset(lex)
- random.shuffle(dataset)
-
-
-
-
-
-
-
- test_size = int(len(dataset) * 0.1)
-
- dataset = np.array(dataset)
-
- train_dataset = dataset[:-test_size]
- test_dataset = dataset[-test_size:]
-
-
-
- n_input_layer = len(lex)
-
- n_layer_1 = 1000
- n_layer_2 = 1000
-
- n_output_layer = 2
-
-
-
- def neural_network(data):
-
- layer_1_w_b = {'w_': tf.Variable(tf.random_normal([n_input_layer, n_layer_1])),
- 'b_': tf.Variable(tf.random_normal([n_layer_1]))}
-
- layer_2_w_b = {'w_': tf.Variable(tf.random_normal([n_layer_1, n_layer_2])),
- 'b_': tf.Variable(tf.random_normal([n_layer_2]))}
-
- layer_output_w_b = {'w_': tf.Variable(tf.random_normal([n_layer_2, n_output_layer])),
- 'b_': tf.Variable(tf.random_normal([n_output_layer]))}
-
-
- layer_1 = tf.add(tf.matmul(data, layer_1_w_b['w_']), layer_1_w_b['b_'])
- layer_1 = tf.nn.relu(layer_1)
- layer_2 = tf.add(tf.matmul(layer_1, layer_2_w_b['w_']), layer_2_w_b['b_'])
- layer_2 = tf.nn.relu(layer_2)
- layer_output = tf.add(tf.matmul(layer_2, layer_output_w_b['w_']), layer_output_w_b['b_'])
-
- return layer_output
-
-
-
- batch_size = 50
-
- X = tf.placeholder('float', [None, len(train_dataset[0][0])])
-
- Y = tf.placeholder('float')
-
-
-
- def train_neural_network(X, Y):
- predict = neural_network(X)
- cost_func = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(predict, Y))
- optimizer = tf.train.AdamOptimizer().minimize(cost_func)
-
- epochs = 13
- with tf.Session() as session:
- session.run(tf.initialize_all_variables())
- epoch_loss = 0
-
- i = 0
- random.shuffle(train_dataset)
- train_x = dataset[:, 0]
- train_y = dataset[:, 1]
-
- for epoch in range(epochs):
- while i < len(train_x):
- start = i
- end = i + batch_size
-
- batch_x = train_x[start:end]
- batch_y = train_y[start:end]
-
- _, c = session.run([optimizer, cost_func], feed_dict={X: list(batch_x), Y: list(batch_y)})
- epoch_loss += c
- i += batch_size
-
- print(epoch, ' : ', epoch_loss)
-
- text_x = test_dataset[:, 0]
- text_y = test_dataset[:, 1]
- correct = tf.equal(tf.argmax(predict, 1), tf.argmax(Y, 1))
- accuracy = tf.reduce_mean(tf.cast(correct, 'float'))
- print('准确率: ', accuracy.eval({X: list(text_x), Y: list(text_y)}))
-
-
- 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词干提取算法,来看例子:
- >>> from nltk.stem.porter import PorterStemmer
- >>> porter_stemmer = PorterStemmer()
- >>> porter_stemmer.stem(‘maximum’)
- u’maximum’
- >>> porter_stemmer.stem(‘presumably’)
- u’presum’
- >>> porter_stemmer.stem(‘multiply’)
- u’multipli’
- >>> porter_stemmer.stem(‘provision’)
- u’provis’
- >>> porter_stemmer.stem(‘owed’)
- u’owe’
Lancaster Stemmer 基于Lancaster 词干提取算法,来看例子
- >>> from nltk.stem.lancaster import LancasterStemmer
- >>> lancaster_stemmer = LancasterStemmer()
- >>> lancaster_stemmer.stem(‘maximum’)
- ‘maxim’
- >>> lancaster_stemmer.stem(‘presumably’)
- ‘presum’
- >>> lancaster_stemmer.stem(‘presumably’)
- ‘presum’
- >>> lancaster_stemmer.stem(‘multiply’)
- ‘multiply’
- >>> lancaster_stemmer.stem(‘provision’)
- u’provid’
- >>> lancaster_stemmer.stem(‘owed’)
- ‘ow’
Snowball Stemmer基于Snowball 词干提取算法,来看例子
- >>> from nltk.stem import SnowballStemmer
- >>> snowball_stemmer = SnowballStemmer(“english”)
- >>> snowball_stemmer.stem(‘maximum’)
- u’maximum’
- >>> snowball_stemmer.stem(‘presumably’)
- u’presum’
- >>> snowball_stemmer.stem(‘multiply’)
- u’multipli’
- >>> snowball_stemmer.stem(‘provision’)
- u’provis’
- >>> snowball_stemmer.stem(‘owed’)
- u’owe’
七 词形还原(lemmatization)
Lemmatisation是把一个任何形式的语言词汇还原为一般形式(能表达完整语义)。相对而言,词干提取是简单的轻量级的词形归并方式,最后获得的结果为词干,并不一定具有实际意义。词形还原处理相对复杂,获得结果为词的原形,能够承载一定意义,与词干提取相比,更具有研究和应用价值。
我们会在后面给出一个同MaxMatch算法相结合的更为复杂的例子。
八 最大匹配算法(MaxMatch)
MaxMatch算法在中文自然语言处理中常常用来进行分词(或许从名字上你已经能想到它是基于贪婪策略设计的一种算法)。通常,英语中一句话里的各个词汇之间通过空格来分割,这是非常straightforward的,但是中文却没有这个遍历。例如“我爱中华人民共和国”,这句话被分词的结果可能是这样的{‘我’,‘爱’,‘中华’,‘人民’,‘共和国’},又或者是{‘我’,‘爱’,‘中华人民共和国’},显然我们更倾向于后者的分词结果。因为‘中华人民共和国’显然是一个专有名词(把这样一个词分割来看显然并不明智)。我们选择后者的策略就是所谓的MaxMatch,即最大匹配。因为‘中华人民共和国’这个词显然要比‘中华’,‘人民’,‘共和国’这些词都长。
我们可以通过一个英文的例子来演示MaxMatch算法(其实中文处理的道理也是一样的)。算法从右侧开始逐渐减少字符串长度,以此求得可能匹配的最大长度的字符串。考虑到我们所获得的词汇可能包含有某种词型的变化,所以其中使用了Lemmatisation,然后在词库里进行匹配查找。
- from nltk.stem import WordNetLemmatizer
- from nltk.corpus import words
-
- wordlist = set(words.words())
- wordnet_lemmatizer = WordNetLemmatizer()
-
- def max_match(text):
- pos2 = len(text)
- result = ''
- while len(text) > 0:
- word = wordnet_lemmatizer.lemmatize(text[0:pos2])
- if word in wordlist:
- result = result + text[0:pos2] + ' '
- text = text[pos2:]
- pos2 = len(text)
- else:
- pos2 = pos2-1
- return result[0:-1]
来看看算法的实现效果
- >>> string = 'theyarebirds'
- >>> print(max_match(string))
- they are birds