import graphlab; #导入包
products = graphlab.SFrame.read_csv('amazon_baby.csv',verbose = False);
#导入表格
products.head(2)#展示
name | review | rating |
---|---|---|
Planetwise Flannel Wipes | These flannel wipes are OK, but in my opinion … |
3 |
Planetwise Wipe Pouch | it came early and was not disappointed. i love … |
5 |
我们使用text_analytics工具,计算产品review的词频,并作为新的一列。
products['word_count'] = graphlab.text_analytics.count_words(products['review'])
#计算词频,放到新的一列,一个dict Sarray结构
products.head(2)#展示
name | review | rating | word_count |
---|---|---|---|
Planetwise Flannel Wipes | These flannel wipes are OK, but in my opinion … |
3 | {‘and’: 5L, ‘stink’: 1L, ’because’: 1L, ‘order … |
Planetwise Wipe Pouch | it came early and was not disappointed. i love … |
5 | {‘and’: 3L, ‘love’: 1L, ’it’: 2L, ‘highly’: 1L, … |
一般来说,我们对评论中的所有单词使用词频计算来训练情感分类器模型。 现在,我们按照相同的套路,但只使用这个评论所有词的一个子集:selected_words。通常, ML的练习者会在训他们的模型之前抛弃认为“不重要”单词。 此过程通常有助于准确性。 然而,在这里,我们将抛掉除上述极少数之外的所有词语。在我们的模型中使 用如此少的单词将损害我们准确性,但可以帮助我们理解我们的分类器在做什么。
selected_words = ['awesome', 'great', 'fantastic',
'amazing', 'love', 'horrible', 'bad',
'terrible', 'awful', 'wow', 'hate']
**对于每条评论的特定词语的一个计数,我们可以利用SF结构的unpack方法来实现,如下:**
#products.unpack('word_count',limit = selected_words,column_name_prefix="",na_value = 0)
当然,为了进一步了解SF和SArray的相关用法,我决定自己动手写一个循环来实现每个单词的计数。对于selected_words词中的每个词,我们构建都一个对于products每一行的word_count项做单词计数的函数,再将这个计数方法apply到products(SF结构同SArray,都具有apply这个方法),形成的结果作为products的新列。 在这个过程中,我们会学习到如何定义函数,以及如何使用SFrame和SArray的apply功能。 我们的目标是创建产品的各个列[‘awesome’,…, ‘hate’] ,其中每行包含相应产品的评论中出现的相关词的次数,如果评论中没有相对应词的次数,则该值为 0。 一种方法是查看每行 ‘word_count’ 列并遵循以下方式: - 如果特定词出现在特定产品的word_count列中,那么我们就知道这个产品评论中出现的这个词的频率。 - 如果特定的词没有出现在word_count中,则应在对应的列下将这个值记为0。 我们可以使用for循环来对 SFrame 的每一行以这个方式计算,进行迭代, 但是种方法会非常慢因为SFrame没有针对使用for循环访问的优化。 相反,我们使用apply方法就会快得很多。
for word in selected_words:
def single_word_count(sf):
dic = sf['word_count']
if word in dic:
return dic[word]
else:
return 0
products[word] = products.apply(single_word_count)
检查产品SFrame,我们应该看到刚刚创建的新列。
products.head(2)
name | review | rating | word_count | awesome | great | fantastic |
---|---|---|---|---|---|---|
Planetwise Flannel Wipes | These flannel wipes are OK, but in my opinion … |
3 | {‘and’: 5L, ‘stink’: 1L, ’because’: 1L, ‘order … |
0 | 0 | 0 |
Planetwise Wipe Pouch | it came early and was not disappointed. i love … |
5 | {‘and’: 3L, ‘love’: 1L, ’it’: 2L, ‘highly’: 1L, … |
0 | 0 | 0 |
amazing | love | horrible | bad | terrible | awful | wow | hate |
---|---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
在我们创建的新列上,使用sum进行求和,当然,因为数据量大,SArray的求和并未进行优化,这个过程将是很漫长的,我们可以使用SFrame.to_dataframe()
和SFrame.to_numpy()
将其转为pandas和numpy的向量进行处理,但是呢,为了熟悉SFrame的数据结构,我还是尽可能不额外引入别的东西。
word_sums = {}
for word in selected_words:
word_sums[word] = products[word].sum()
我们可以看看,在selected_words中,哪个词在评论中出现得最多,哪个词出现得最少。我们可以看出,在评论中great出现的次数最多,为45206次,wow出现最少,仅为144次。
print word_sums
print max(word_sums, key=word_sums.get)
print min(word_sums, key=word_sums.get)
{‘fantastic’: 932L, ‘love’: 42065L, ‘bad’: 3724L, ‘awesome’: 2090L, ‘great’: 45206L, ‘terrible’: 748L, ‘amazing’: 1363L, ‘horrible’: 734L, ‘awful’: 383L, ‘hate’: 1220L, ‘wow’: 144L} great wow
我们创建新列,作为敏感度,1表示’rating’值大等于4,否则为0。我们也对数据集按0.8比例进行分割。
products['sentiment'] = products['rating'] >=4
train_data,test_data = products.random_split(.8, seed=0)
同一般的思路,我们现在仅使用selected_words作为评价的特征,来创建新的情绪分析模型。模型中指定`features= selected_words`,表示使用这些特征(列)来训练模型。
selected_words_model = graphlab.logistic_classifier.create(train_data,
target='sentiment',
features= selected_words,
validation_set=test_data);#按词频和敏感度做逻辑回归分类
接下来,我们可以使用coefficients功能来查看每个单词分配的权重,有一个直观上的感觉。结果中,有一个value列,从value列中,我们可以清晰地看到,awesome等单词提供了正的贡献,而horrible等单词对结果提供了负的贡献。而且直观地看,确实有些词语气比较重,确实贡献程度比较大。这是符合我们直观的感觉的。
coe = selected_words_model['coefficients']
print coe
coe.show();
进一步,我们还可以对每个词的权重进行一个排序,如下:
coe.sort('value').print_rows(num_rows=12, num_columns=4)
+————-+——-+——-+—————–+—–+ | name | index | class | value | … | +————-+——-+——-+—————–+—–+ | horrible | None | 1 | -1.75774666269 | … | | terrible | None | 1 | -1.73568678121 | … | | awful | None | 1 | -1.43812417008 | … | | hate | None | 1 | -1.28297165251 | … | | bad | None | 1 | -0.904090478755 | … | | wow | None | 1 | 0.324810598259 | … | | fantastic | None | 1 | 0.699443114061 | … | | great | None | 1 | 0.699683829426 | … | | amazing | None | 1 | 0.892025920947 | … | | (intercept) | None | 1 | 0.899735119511 | … | | awesome | None | 1 | 1.00625649388 | … | | love | None | 1 | 1.15720106058 | … | +————-+——-+——-+—————–+—–+ [12 rows x 5 columns]
我们可以使用ROC曲线做一个简单的评估,从曲线中可以看出,AUC的值为0.643。
selected_words_model.evaluate(test_data, metric='roc_curve')#模型评价,使用roc曲线作为度量,auc为下方的面积
selected_words_model.show(view='Evaluation')#展示roc曲线
Canvas is updated and available in a tab in the default browser. 我们再使用全部的词来模型,评估其效能。可以看出其AUC的值为0.907,远高于我们选部分词的情况。为什么会发生这种情况呢?我们可以做进一步的分析。
sentiment_model = graphlab.logistic_classifier.create(train_data,
target='sentiment',
features=['word_count'],
validation_set=test_data);#按词频和敏感度做逻辑回归分类
sentiment_model.evaluate(test_data, metric='roc_curve');#模型评价,使用roc曲线作为度量,auc为下方的面积
sentiment_model.show(view='Evaluation');#展示roc曲线
我们将研究一款名为“Baby Trend Diaper Champ”的产品,这是用于处理婴儿尿布的垃圾桶,可以保持气味清新。
disper_champ_reviews = products[products['name'] == 'Baby Trend Diaper Champ']
disper_champ_reviews.head(2)
name | review | rating | word_count | awesome | great | fantastic |
---|---|---|---|---|---|---|
Baby Trend Diaper Champ | Ok - newsflash. Diapers are just smelly. We’ve … |
4 | {‘just’: 2L, ‘less’: 1L, ’-‘: 3L, ‘smell- … |
0 | 0 | 0 |
Baby Trend Diaper Champ | This is a good product to start and very easy to … |
3 | {‘and’: 3L, ‘because’: 1L, ‘old’: 1L, ‘use.’: … |
0 | 0 | 0 |
amazing | love | horrible | bad | terrible | awful | wow | hate | sentiment |
---|---|---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
我们使用sentiment_model预测disper_champ_reviews每个评论的情绪,并根据predicted_sentiment的值大小进行排序。
所谓的predicted_sentiment,就是那条review所表达的情绪属于是正面的,即属于1(评分大等于4)的一个概率值。
disper_champ_reviews['predicted_sentiment'] =sentiment_model.predict(disper_champ_reviews, output_type='probability')
#做出逻辑回归
disper_champ_reviews = disper_champ_reviews.sort('predicted_sentiment', ascending=False)#按敏感度下降排序
disper_champ_reviews.head(2)
name | review | rating | word_count | awesome | great | fantastic |
---|---|---|---|---|---|---|
Baby Trend Diaper Champ | Diaper Champ or Diaper Genie? That was my … |
5 | {‘all’: 1L, ‘bags.’: 1L, ’son,’: 1L, ‘(i’: 1L, … |
0 | 0 | 0 |
Baby Trend Diaper Champ | Baby Luke can turn a clean diaper to a dirty … |
5 | {‘all’: 1L, ‘less’: 1L, ”friend’s”: 1L, ‘(whi … |
0 | 0 | 0 |
amazing | love | horrible | bad | terrible | awful | wow | hate | sentiment | predicted_sentiment |
---|---|---|---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0.999999830319 |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0.999999781997 |
现在我们使用selected_words_model来预测上面列出的对Baby Trend Diaper Champ评价最积极的那一项。可以看出它的结果为1,表示,这是一个100%好的一个评价,但是一般来说,我们取概率值都是趋向于1,甚至无限接近,一般取不到1(比如说sigmoid函数作为概率取值),说明选部分词做模型预测,确实精度不够。
print selected_words_model.predict(disper_champ_reviews [0])
[1L]