自然语言处理之情感分析实战(分类问题)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lusongno1/article/details/81024391
# 基于自然语言处理的顾客对于产品的情感分析(分类问题) > 在这个项目中,我们来研究分类问题,并将它们应用于分析顾客对于物品的情绪。进一步,我们要理解分类产生的错误类型。 我们使用ipython notebook,借助GraphLab的包,来分析人们对于真实产品的评论中所蕴含的情绪。 ###### 在这个任务中,我们将进一步探索,我们不再使用review中的全部词,而使用一组关键词来训练情绪分析模型,并且验证学习到模型中,这些单词的权重,并将这个简单的分类器的结果与使用所有词的分类器的结果进行比较。 首先,导入数据,`verbose = False`表示我不打印读取的进程,这样让界面看得更简洁一点。展示前两个记录,看看数据。
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
[2 rows x 3 columns]

我们使用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, …
[2 rows x 4 columns]

一般来说,我们对评论中的所有单词使用词频计算来训练情感分类器模型。 现在,我们按照相同的套路,但只使用这个评论所有词的一个子集: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
[2 rows x 15 columns]

在我们创建的新列上,使用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
[2 rows x 16 columns]

我们使用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
[2 rows x 17 columns]

现在我们使用selected_words_model来预测上面列出的对Baby Trend Diaper Champ评价最积极的那一项。可以看出它的结果为1,表示,这是一个100%好的一个评价,但是一般来说,我们取概率值都是趋向于1,甚至无限接近,一般取不到1(比如说sigmoid函数作为概率取值),说明选部分词做模型预测,确实精度不够。

print selected_words_model.predict(disper_champ_reviews [0])
[1L]

猜你喜欢

转载自blog.csdn.net/lusongno1/article/details/81024391