经典NLP案例 | 推文评论情绪分析:从数据预处理到模型构建的全面指南

news2024/12/16 18:49:26

NLP经典案例:推文评论情绪提取

项目背景

“My ridiculous dog is amazing.” [sentiment: positive]

由于所有推文每秒都在传播,很难判断特定推文背后的情绪是否会影响一家公司或一个人的品牌,因为它的病毒式传播(积极),或者因为它的负面基调而破坏利润。在决策和反应在几秒钟内创建和更新的时代,用语言捕捉情感非常重要。但是,哪些词实际上导致了情感描述呢?在本次比赛中,您需要选出推文中反映情绪的部分(单词或短语)。

利用这个广泛的推文数据集帮助您培养在这一重要领域的技能。努力提高你的技术,在这场比赛中占据榜首。推文中的哪些词语支持积极、消极或中立的情绪?您如何使用机器学习工具帮助做出决定?

在本次比赛中,我们从 Figure Eight 的 Data for Everyone 平台中提取了支持短语。该数据集的标题为“情感分析:具有现有情感标签的文本推文中的情感”,此处在知识共享署名 4.0 下使用。国际许可证。你在这次比赛中的目标是构建一个可以做同样事情的模型 - 查看给定推文的标记情绪,并找出最能支持它的单词或短语。

目录

  • EDA(数据探索性分析)
  • 语料库清洗
  • 词频可视化
  • 词云图
  • NER模型建立

关于本项目

在此笔记中,我将简要解释数据集的结构。我将生成并分析元特征。然后,我将使用 Matplotlib、seaborn 和 Plotly 可视化数据集,以获得尽可能多的见解。我还将把这个问题作为 NER 问题来处理来构建模型

导入必要的库

import re
import string
import numpy as np 
import random
import pandas as pd 
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline
from plotly import graph_objs as go
import plotly.express as px
import plotly.figure_factory as ff
from collections import Counter

from PIL import Image
from wordcloud import WordCloud, STOPWORDS, ImageColorGenerator

import nltk
from nltk.corpus import stopwords

from tqdm import tqdm
import os
import nltk
import spacy
import random
from spacy.util import compounding
from spacy.util import minibatch

import warnings
warnings.filterwarnings("ignore")

import os
for dirname, _, filenames in os.walk('/kaggle/input'):
    for filename in filenames:
        print(os.path.join(dirname, filename))

# 您写入当前目录的任何结果都将保存为输出。

下面是一个辅助函数,它生成随机颜色,可用于为您的绘图提供不同的颜色。随意使用它

def random_colours(number_of_colors):
    '''
    随机颜色生成的简单函数。
    输入:
        number_of_colors -指示将生成的颜色数量的整数值。
    输出:
        采用以下格式的颜色: ['#E86DA4'] 。
    '''
    colors = []
    for i in range(number_of_colors):
        colors.append("#"+''.join([random.choice('0123456789ABCDEF') for j in range(6)]))
    return colors

读取数据

本项目数据集来源于kaggle竞赛平台,可公众号后台回复“推文情绪”即可获取数据集

train = pd.read_csv('/kaggle/input/tweet-sentiment-extraction/train.csv')
test = pd.read_csv('/kaggle/input/tweet-sentiment-extraction/test.csv')
ss = pd.read_csv('/kaggle/input/tweet-sentiment-extraction/sample_submission.csv')
print(train.shape)
print(test.shape)
(27481, 4)
(3534, 3)

所以我们在训练集中有 27486 条推文,在测试集中有 3535 条推文

train.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 27481 entries, 0 to 27480
Data columns (total 4 columns):
textID           27481 non-null object
text             27480 non-null object
selected_text    27480 non-null object
sentiment        27481 non-null object
dtypes: object(4)
memory usage: 858.9+ KB

我们在训练中有一个空值,因为值的测试字段是 NAN,我们将删除它

train.dropna(inplace=True)
test.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 3534 entries, 0 to 3533
Data columns (total 3 columns):
textID       3534 non-null object
text         3534 non-null object
sentiment    3534 non-null object
dtypes: object(3)
memory usage: 83.0+ KB

测试集中不存在空值

EDA(数据探索性分析)

train.head()

Selected_text 是文本的子集

train.describe()

让我们看看推文在训练集中的分布

temp = train.groupby('sentiment').count()['text'].reset_index().sort_values(by='text',ascending=False)
temp.style.background_gradient(cmap='Purples')
sentimenttext
1neutral11117
2positive8582
0negative7781
plt.figure(figsize=(12,6))
sns.countplot(x='sentiment',data=train)

让我们画一个漏斗图以获得更好的可视化效果

fig = go.Figure(go.Funnelarea(
    text =temp.sentiment,
    values = temp.text,
    title = {"position": "top center", "text": "Funnel-Chart of Sentiment Distribution"}
    ))
fig.show()

目前我们对数据了解多少:

在开始之前,让我们看一下我们已经了解的有关数据的一些知识,这些知识将帮助我们获得更多新的见解:

  • 我们知道 selected_text 是文本的子集
  • 我们知道 selected_text 只包含一段文本,即,它不会在两个句子之间跳转。例如:-如果文本是“在与供应商的会议中度过了整个上午,而我的老板对此不满意”他们。很有趣。 我早上还有其他计划”所选文本可以是“我的老板对他们不满意”。很有趣”或“很有趣”,但不能是“早上好,供应商和我的老板,
  • 我们知道中性推文的文本和 selected_text 之间的 jaccard 相似度为 97%
  • 另外,有些行中 selected_text 从单词之间开始,因此 selected_texts 并不总是有意义,因为我们不知道是否测试集的输出是否包含这些差异,我们不确定预处理和删除标点符号是否是一个好主意

生成元特征

在本笔记本的先前版本中,我使用所选文本和主要文本中的单词数、文本中的单词长度和选择作为主要元特征,但在本次比赛的背景下,我们必须预测 selected_text 这是一个文本的子集,生成的更有用的功能将是

  • Selected_text 和 Text 的字数差异
  • 文本和 Selected_text 之间的 Jaccard 相似度分数

因此,生成我们之前使用过的特征对我们来说没有用,因为它们在这里并不重要

以下是 Jaccard 相似度的介绍:https://www.geeksforgeeks.org/find-the-jaccard-index-and-jaccard-distance-Between-the-two-given-sets/

def jaccard(str1, str2): 
    a = set(str1.lower().split()) 
    b = set(str2.lower().split())
    c = a.intersection(b)
    return float(len(c)) / (len(a) + len(b) - len(c))
results_jaccard=[]

for ind,row in train.iterrows():
    sentence1 = row.text
    sentence2 = row.selected_text

    jaccard_score = jaccard(sentence1,sentence2)
    results_jaccard.append([sentence1,sentence2,jaccard_score])
jaccard = pd.DataFrame(results_jaccard,columns=["text","selected_text","jaccard_score"])
train = train.merge(jaccard,how='outer')
train['Num_words_ST'] = train['selected_text'].apply(lambda x:len(str(x).split())) #Number Of words in Selected Text
train['Num_word_text'] = train['text'].apply(lambda x:len(str(x).split())) #Number Of words in main text
train['difference_in_words'] = train['Num_word_text'] - train['Num_words_ST'] #Difference in Number of words text and Selected Text
train.head()

我们看一下Meta-Features的分布

hist_data = [train['Num_words_ST'],train['Num_word_text']]

group_labels = ['Selected_Text', 'Text']

# 使用自定义 bin_size 创建 distplot
fig = ff.create_distplot(hist_data, group_labels,show_curve=False)
fig.update_layout(title_text='Distribution of Number Of words')
fig.update_layout(
    autosize=False,
    width=900,
    height=700,
    paper_bgcolor="LightSteelBlue",
)
fig.show()

  • 字数图非常有趣,字数大于 25 的推文非常少,因此字数分布图是右偏的
plt.figure(figsize=(12,6))
p1=sns.kdeplot(train['Num_words_ST'], shade=True, color="r").set_title('Kernel Distribution of Number Of words')
p1=sns.kdeplot(train['Num_word_text'], shade=True, color="b")

现在看到不同情绪的字数和jaccard分数的差异会更有趣

plt.figure(figsize=(12,6))
p1=sns.kdeplot(train[train['sentiment']=='positive']['difference_in_words'], shade=True, color="b").set_title('Kernel Distribution of Difference in Number Of words')
p2=sns.kdeplot(train[train['sentiment']=='negative']['difference_in_words'], shade=True, color="r")

在这里插入图片描述

plt.figure(figsize=(12,6))
sns.distplot(train[train['sentiment']=='neutral']['difference_in_words'],kde=False)

我无法绘制中性推文的 kde 图,因为大多数字数差异值为零。我们现在可以清楚地看到这一点,如果我们一开始就使用了该功能,我们就会知道对于中性推文来说,文本和所选文本大多相同,因此在执行 EDA 时牢记最终目标始终很重要

plt.figure(figsize=(12,6))
p1=sns.kdeplot(train[train['sentiment']=='positive']['jaccard_score'], shade=True, color="b").set_title('KDE of Jaccard Scores across different Sentiments')
p2=sns.kdeplot(train[train['sentiment']=='negative']['jaccard_score'], shade=True, color="r")
plt.legend(labels=['positive','negative'])
<matplotlib.legend.Legend at 0x7f99682eac50>

出于同样的原因,我无法绘制中立推文的 jaccard_scores 的 kde,因此我将绘制分布图

plt.figure(figsize=(12,6))
sns.distplot(train[train['sentiment']=='neutral']['jaccard_score'],kde=False)

我们可以在这里看到一些有趣的趋势:

  • 正面和负面推文具有高峰度,因此值集中在窄和高密度两个区域
  • 中性推文具有较低的峰度值,并且其密度波动接近 1

定义解释:

  • 峰度是分布的峰值程度以及围绕该峰值的分布程度的度量
  • 偏度衡量曲线偏离正态分布的程度

EDA结论

我们可以从 jaccard 分数图中看到,分数为 1 附近有负图和正图的峰值。这意味着存在一组推文,其中文本和所选文本之间存在高度相似性,如果我们可以找到这些聚类,那么我们可以预测这些推文的选定文本的文本,无论片段如何

让我们看看是否可以找到这些簇,一个有趣的想法是检查文本中单词数少于 3 个的推文,因为那里的文本可能完全用作文本

k = train[train['Num_word_text']<=2]
k.groupby('sentiment').mean()['jaccard_score']
sentiment
negative    0.788580
neutral     0.977805
positive    0.765700
Name: jaccard_score, dtype: float64

我们可以看到文本和所选文本之间存在相似性。让我们仔细看看

k[k['sentiment']=='positive']

因此很明显,大多数时候,文本被用作选定文本。我们可以通过预处理字长小于3的文本来改进这一点。我们将记住这些信息并在模型构建中使用它。

清理语料库

现在,在我们深入从文本和选定文本中的单词中提取信息之前,让我们首先清理数据。

def clean_text(text):
    '''将文本设为小写、删除方括号中的文本、删除链接、删除标点符号
    并删除包含数字的单词。'''
    text = str(text).lower()
    text = re.sub('\[.*?\]', '', text)
    text = re.sub('https?://\S+|www\.\S+', '', text)
    text = re.sub('<.*?>+', '', text)
    text = re.sub('[%s]' % re.escape(string.punctuation), '', text)
    text = re.sub('\n', '', text)
    text = re.sub('\w*\d\w*', '', text)
    return text
train['text'] = train['text'].apply(lambda x:clean_text(x))
train['selected_text'] = train['selected_text'].apply(lambda x:clean_text(x))
train.head()

我们的目标选定文本中最常见的单词

train['temp_list'] = train['selected_text'].apply(lambda x:str(x).split())
top = Counter([item for sublist in train['temp_list'] for item in sublist])
temp = pd.DataFrame(top.most_common(20))
temp.columns = ['Common_words','count']
temp.style.background_gradient(cmap='Blues')
IndexCommon_wordscount
0i7200
1to5305
2the4590
3a3538
4my2783
5you2624
6and2321
7it2158
8is2115
9in1986
10for1854
11im1676
12of1638
13me1540
14on1488
15so1410
16have1345
17that1297
18but1267
19good1251
fig = px.bar(temp, x="count", y="Common_words", title='Commmon Words in Selected Text', orientation='h', 
             width=700, height=700,color='Common_words')
fig.show()

哎呀!当我们清理数据集时,我们没有删除停用词,因此我们可以看到最常见的词是 ‘to’ 。删除停用词后重试

def remove_stopword(x):
    return [y for y in x if y not in stopwords.words('english')]
train['temp_list'] = train['temp_list'].apply(lambda x:remove_stopword(x))
top = Counter([item for sublist in train['temp_list'] for item in sublist])
temp = pd.DataFrame(top.most_common(20))
temp = temp.iloc[1:,:]
temp.columns = ['Common_words','count']
temp.style.background_gradient(cmap='Purples')
IndexCommon_wordscount
1good1251
2day1058
3love909
4happy852
5like774
6get772
7dont765
8go700
9cant613
10work612
11going592
12today564
13got558
14one538
15time534
16thanks532
17lol528
18really520
19u519
fig = px.treemap(temp, path=['Common_words'], values='count',title='Tree of Most Common Words')
fig.show()

文本中最常见的单词

我们还看一下 Text 中最常见的单词

train['temp_list1'] = train['text'].apply(lambda x:str(x).split()) #文本每行的单词列表
train['temp_list1'] = train['temp_list1'].apply(lambda x:remove_stopword(x)) #删除停用词
top = Counter([item for sublist in train['temp_list1'] for item in sublist])
temp = pd.DataFrame(top.most_common(25))
temp = temp.iloc[1:,:]
temp.columns = ['Common_words','count']
temp.style.background_gradient(cmap='Blues')
IndexCommon_wordscount
1day2044
2good1549
3get1426
4like1346
5go1267
6dont1200
7love1122
8work1112
9going1096
10today1096
11got1072
12cant1020
13happy976
14one971
15lol948
16time942
17know930
18u923
19really908
20back891
21see797
22well744
23new740
24night737

所以前两个常见词是我,所以我删除了它并从第二行获取数据

fig = px.bar(temp, x="count", y="Common_words", title='Commmon Words in Text', orientation='h', 
             width=700, height=700,color='Common_words')
fig.show()

所以我们可以看到所选文本和文本中最常见的单词几乎相同,这是显而易见的

最常用的词 情感明智

让我们看看不同情绪中最常见的单词

Positive_sent = train[train['sentiment']=='positive']
Negative_sent = train[train['sentiment']=='negative']
Neutral_sent = train[train['sentiment']=='neutral']
#最常见的正面词汇
top = Counter([item for sublist in Positive_sent['temp_list'] for item in sublist])
temp_positive = pd.DataFrame(top.most_common(20))
temp_positive.columns = ['Common_words','count']
temp_positive.style.background_gradient(cmap='Greens')
IndexCommon_wordscount
0good826
1happy730
2love697
3day456
4thanks439
5great364
6fun287
7nice267
8mothers259
9hope245
10awesome232
11im185
12thank180
13like167
14best154
15wish152
16amazing135
17really128
18better125
19cool119
fig = px.bar(temp_positive, x="count", y="Common_words", title='Most Commmon Positive Words', orientation='h', 
             width=700, height=700,color='Common_words')
fig.show()

#最常见的负面词
top = Counter([item for sublist in Negative_sent['temp_list'] for item in sublist])
temp_negative = pd.DataFrame(top.most_common(20))
temp_negative = temp_negative.iloc[1:,:]
temp_negative.columns = ['Common_words','count']
temp_negative.style.background_gradient(cmap='Reds')
IndexCommon_wordscount
1miss358
2sad343
3sorry300
4bad246
5hate230
6dont221
7cant201
8sick166
9like162
10sucks159
11feel158
12tired144
13really137
14good127
15bored115
16day110
17hurts108
18work99
19get97
fig = px.treemap(temp_negative, path=['Common_words'], values='count',title='Tree Of Most Common Negative Words')
fig.show()

#最常见的中性词
top = Counter([item for sublist in Neutral_sent['temp_list'] for item in sublist])
temp_neutral = pd.DataFrame(top.most_common(20))
temp_neutral = temp_neutral.loc[1:,:]
temp_neutral.columns = ['Common_words','count']
temp_neutral.style.background_gradient(cmap='Reds')
IndexCommon_wordscount
1get612
2go569
3day492
4dont482
5going472
6work467
7like445
8got441
9today427
10lol427
11time413
12know407
13back402
14one394
15u376
16see349
17cant339
18home335
19want319
fig = px.bar(temp_neutral, x="count", y="Common_words", title='Most Commmon Neutral Words', orientation='h', 
             width=700, height=700,color='Common_words')
fig.show()

fig = px.treemap(temp_neutral, path=['Common_words'], values='count',title='Tree Of Most Common Neutral Words')
fig.show()

我们可以看到 get、go、dont、got、u、cant、lol、like 等词在这三个细分市场中都很常见。这很有趣,因为像 dont 和 cant 这样的词更多的是消极的性质,而像 lol 这样的词更多的是积极的性质。这是否意味着我们的数据被错误地标记了,在 N-gram 分析后我们将对此有更多的见解

看到不同情绪所特有的词会很有趣

让我们看看每个片段中的独特单词

我们将按以下顺序查看每个片段中的唯一单词:

  • 积极的
  • 消极的
  • 中性的
raw_text = [word for word_list in train['temp_list1'] for word in word_list]
def words_unique(sentiment,numwords,raw_words):
    '''
    输入:
        细分 -细分类别(例如“中性”);
        numwords -您希望在最终结果中看到多少个特定单词; 
        raw_words -train_data[train_data.segments == snippets]['temp_list1'] 中的项目列表:
    输出: 
        数据框提供有关特定成分名称及其在所选菜肴中出现的次数的信息(根据其计数按降序排列)..

    '''
    allother = []
    for item in train[train.sentiment != sentiment]['temp_list1']:
        for word in item:
            allother .append(word)
    allother  = list(set(allother ))
    
    specificnonly = [x for x in raw_text if x not in allother]
    
    mycounter = Counter()
    
    for item in train[train.sentiment == sentiment]['temp_list1']:
        for word in item:
            mycounter[word] += 1
    keep = list(specificnonly)
    
    for word in list(mycounter):
        if word not in keep:
            del mycounter[word]
    
    Unique_words = pd.DataFrame(mycounter.most_common(numwords), columns = ['words','count'])
    
    return Unique_words

积极的推文

Unique_Positive= words_unique('positive', 20, raw_text)
print("The top 20 unique words in Positive Tweets are:")
Unique_Positive.style.background_gradient(cmap='Greens')
The top 20 unique words in Positive Tweets are:
Indexwordscount
0congratulations29
1thnx10
2appreciated8
3shared7
4presents7
5greetings7
6blessings6
7mothersday6
8mcr6
9coolest6
10honored6
11goood6
12wango5
13actress5
14mint5
15dayyyy5
16ciara5
17twin5
18kudos5
19hurray5
fig = px.treemap(Unique_Positive, path=['words'], values='count',title='Tree Of Unique Positive Words')
fig.show()

from palettable.colorbrewer.qualitative import Pastel1_7
plt.figure(figsize=(16,10))
my_circle=plt.Circle((0,0), 0.7, color='white')
plt.pie(Unique_Positive['count'], labels=Unique_Positive.words, colors=Pastel1_7.hex_colors)
p=plt.gcf()
p.gca().add_artist(my_circle)
plt.title('DoNut Plot Of Unique Positive Words')
plt.show()

Unique_Negative= words_unique('negative', 10, raw_text)
print("The top 10 unique words in Negative Tweets are:")
Unique_Negative.style.background_gradient(cmap='Reds')
The top 10 unique words in Negative Tweets are:
Indexwordscount
0ache12
1suffering9
2allergic7
3cramps7
4saddest7
5pissing7
6sob6
7dealing6
8devastated6
9noes6
from palettable.colorbrewer.qualitative import Pastel1_7
plt.figure(figsize=(16,10))
my_circle=plt.Circle((0,0), 0.7, color='white')
plt.rcParams['text.color'] = 'black'
plt.pie(Unique_Negative['count'], labels=Unique_Negative.words, colors=Pastel1_7.hex_colors)
p=plt.gcf()
p.gca().add_artist(my_circle)
plt.title('DoNut Plot Of Unique Negative Words')
plt.show()

Unique_Neutral= words_unique('neutral', 10, raw_text)
print("The top 10 unique words in Neutral Tweets are:")
Unique_Neutral.style.background_gradient(cmap='Oranges')
The top 10 unique words in Neutral Tweets are:
Indexwordscount
0settings9
1explain7
2mite6
3hiya6
4reader5
5pr5
6sorta5
7fathers5
8enterprise5
9guessed5
from palettable.colorbrewer.qualitative import Pastel1_7
plt.figure(figsize=(16,10))
my_circle=plt.Circle((0,0), 0.7, color='white')
plt.pie(Unique_Neutral['count'], labels=Unique_Neutral.words, colors=Pastel1_7.hex_colors)
p=plt.gcf()
p.gca().add_artist(my_circle)
plt.title('DoNut Plot Of Unique Neutral Words')
plt.show()

通过查看每种情绪的独特单词,我们现在对数据有了更清晰的了解,这些独特的单词是推文情绪的强有力决定因素

词云图绘制

我们将按以下顺序构建词云:

  • 中性推文的词云
  • 积极推文的词云
  • 负面推文的词云
def plot_wordcloud(text, mask=None, max_words=200, max_font_size=100, figure_size=(24.0,16.0), color = 'white',
                   title = None, title_size=40, image_color=False):
    stopwords = set(STOPWORDS)
    more_stopwords = {'u', "im"}
    stopwords = stopwords.union(more_stopwords)

    wordcloud = WordCloud(background_color=color,
                    stopwords = stopwords,
                    max_words = max_words,
                    max_font_size = max_font_size, 
                    random_state = 42,
                    width=400, 
                    height=200,
                    mask = mask)
    wordcloud.generate(str(text))
    
    plt.figure(figsize=figure_size)
    if image_color:
        image_colors = ImageColorGenerator(mask);
        plt.imshow(wordcloud.recolor(color_func=image_colors), interpolation="bilinear");
        plt.title(title, fontdict={'size': title_size,  
                                  'verticalalignment': 'bottom'})
    else:
        plt.imshow(wordcloud);
        plt.title(title, fontdict={'size': title_size, 'color': 'black', 
                                  'verticalalignment': 'bottom'})
    plt.axis('off');
    plt.tight_layout()  
d = '/kaggle/input/masks-for-wordclouds/'

我添加了更多单词,如 im 、 u (我们说这些单词出现在最常见的单词中,干扰了我们的分析)作为停用词

中性推文云

我们已经形象化了最常见的否定词,但词云为我们提供了更多的清晰度

pos_mask = np.array(Image.open(d+ 'twitter_mask.png'))
plot_wordcloud(Neutral_sent.text,mask=pos_mask,color='white',max_font_size=100,title_size=30,title="WordCloud of Neutral Tweets")

plot_wordcloud(Positive_sent.text,mask=pos_mask,title="Word Cloud Of Positive tweets",title_size=30)

plot_wordcloud(Negative_sent.text,mask=pos_mask,title="Word Cloud of Negative Tweets",color='white',title_size=30)

模型构建

1) 将问题建模为 NER

命名实体识别 (NER) 是一个标准的 NLP 问题,涉及从文本块中识别命名实体(人物、地点、组织等),并将它们分类为一组预定义的类别。
为了理解 NER,这里有一篇非常好的文章:https://towardsdatascience.com/named-entity-recognition-with-nltk-and-spacy-8c4a7d88e7da

我们将使用 spacy 来创建我们自己的定制 NER 模型(针对每个情绪单独)。这种方法的动机当然是 Rohit Singh 共享的内核。

我的解决方案有何不同:

  • 由于 jaccard 的高度相似性,我将使用文本作为所有中性推文的 selected_text
  • 此外,我还将使用文本作为文本中字数少于 3 个的所有推文的 selected_text,如前所述
  • 我将为正面和负面推文训练两种不同的模型
  • 我不会预处理数据,因为所选文本包含原始文本
df_train = pd.read_csv('/kaggle/input/tweet-sentiment-extraction/train.csv')
df_test = pd.read_csv('/kaggle/input/tweet-sentiment-extraction/test.csv')
df_submission = pd.read_csv('/kaggle/input/tweet-sentiment-extraction/sample_submission.csv')
df_train['Num_words_text'] = df_train['text'].apply(lambda x:len(str(x).split())) #训练集中正文中的字数
df_train = df_train[df_train['Num_words_text']>=3]

要完全了解如何使用自定义输入训练 spacy NER,请阅读 spacy 文档以及本笔记本中的代码演示:https://spacy.io/usage/training#ner 按照更新 Spacy NER

def save_model(output_dir, nlp, new_model_name):
    ''' This Function Saves model to 
    given output directory'''
    
    output_dir = f'../working/{output_dir}'
    if output_dir is not None:        
        if not os.path.exists(output_dir):
            os.makedirs(output_dir)
        nlp.meta["name"] = new_model_name
        nlp.to_disk(output_dir)
        print("Saved model to", output_dir)
# 如果你想在现有模型上进行训练,请传递 model = nlp

def train(train_data, output_dir, n_iter=20, model=None):
    """Load the model, set up the pipeline and train the entity recognizer."""
    ""
    if model is not None:
        nlp = spacy.load(output_dir)  # load existing spaCy model
        print("Loaded model '%s'" % model)
    else:
        nlp = spacy.blank("en")  # create blank Language class
        print("Created blank 'en' model")
    
# 创建内置管道组件并将其添加到管道中
    # nlp.create_pipe 适用于在 spaCy 中注册的内置程序
    if "ner" not in nlp.pipe_names:
        ner = nlp.create_pipe("ner")
        nlp.add_pipe(ner, last=True)
    # 否则,获取它以便我们添加标签
    else:
        ner = nlp.get_pipe("ner")
    
    # 添加标签
    for _, annotations in train_data:
        for ent in annotations.get("entities"):
            ner.add_label(ent[2])

    # 获取其他管道的名称以在训练期间禁用它们
    other_pipes = [pipe for pipe in nlp.pipe_names if pipe != "ner"]
    with nlp.disable_pipes(*other_pipes):  # only train NER
        # sizes = compounding(1.0, 4.0, 1.001)
        # batch up the examples using spaCy's minibatch
        if model is None:
            nlp.begin_training()
        else:
            nlp.resume_training()


        for itn in tqdm(range(n_iter)):
            random.shuffle(train_data)
            batches = minibatch(train_data, size=compounding(4.0, 500.0, 1.001))    
            losses = {}
            for batch in batches:
                texts, annotations = zip(*batch)
                nlp.update(texts,  # batch of texts
                            annotations,  # batch of annotations
                            drop=0.5,   # dropout - make it harder to memorise data
                            losses=losses, 
                            )
            print("Losses", losses)
    save_model(output_dir, nlp, 'st_ner')
def get_model_out_path(sentiment):
    '''
    Returns Model output path
    '''
    model_out_path = None
    if sentiment == 'positive':
        model_out_path = 'models/model_pos'
    elif sentiment == 'negative':
        model_out_path = 'models/model_neg'
    return model_out_path
def get_training_data(sentiment):
    '''
    Returns Trainong data in the format needed to train spacy NER
    '''
    train_data = []
    for index, row in df_train.iterrows():
        if row.sentiment == sentiment:
            selected_text = row.selected_text
            text = row.text
            start = text.find(selected_text)
            end = start + len(selected_text)
            train_data.append((text, {"entities": [[start, end, 'selected_text']]}))
    return train_data
正面和负面推文的训练模型
sentiment = 'positive'

train_data = get_training_data(sentiment)
model_path = get_model_out_path(sentiment)
# 出于演示目的,我进行了 3 次迭代,您可以根据需要训练模型
train(train_data, model_path, n_iter=3, model=None)
Created blank 'en' model


 33%|███▎      | 1/3 [00:52<01:45, 52.84s/it]

Losses {'ner': 33900.41114743876}


 67%|██████▋   | 2/3 [01:45<00:52, 52.68s/it]

Losses {'ner': 30517.31436416012}


100%|██████████| 3/3 [02:37<00:00, 52.62s/it]

Losses {'ner': 28912.80112953766}
Saved model to ../working/models/model_pos
sentiment = 'negative'

train_data = get_training_data(sentiment)
model_path = get_model_out_path(sentiment)

train(train_data, model_path, n_iter=3, model=None)
  0%|          | 0/3 [00:00<?, ?it/s]

Created blank 'en' model


 33%|███▎      | 1/3 [00:50<01:41, 50.56s/it]

Losses {'ner': 31926.559298905544}


 67%|██████▋   | 2/3 [01:40<00:50, 50.32s/it]

Losses {'ner': 28526.67285699508}


100%|██████████| 3/3 [02:30<00:00, 50.02s/it]

Losses {'ner': 27071.952286979005}
Saved model to ../working/models/model_neg

使用经过训练的模型进行预测

def predict_entities(text, model):
    doc = model(text)
    ent_array = []
    for ent in doc.ents:
        start = text.find(ent.text)
        end = start + len(ent.text)
        new_int = [start, end, ent.label_]
        if new_int not in ent_array:
            ent_array.append([start, end, ent.label_])
    selected_text = text[ent_array[0][0]: ent_array[0][1]] if len(ent_array) > 0 else text
    return selected_text
selected_texts = []
MODELS_BASE_PATH = '../input/tse-spacy-model/models/'

if MODELS_BASE_PATH is not None:
    print("Loading Models  from ", MODELS_BASE_PATH)
    model_pos = spacy.load(MODELS_BASE_PATH + 'model_pos')
    model_neg = spacy.load(MODELS_BASE_PATH + 'model_neg')
        
    for index, row in df_test.iterrows():
        text = row.text
        output_str = ""
        if row.sentiment == 'neutral' or len(text.split()) <= 2:
            selected_texts.append(text)
        elif row.sentiment == 'positive':
            selected_texts.append(predict_entities(text, model_pos))
        else:
            selected_texts.append(predict_entities(text, model_neg))
        
df_test['selected_text'] = selected_texts
Loading Models  from  ../input/tse-spacy-model/models/
df_submission['selected_text'] = df_test['selected_text']
df_submission.to_csv("submission.csv", index=False)
display(df_submission.head(10))
IndextextIDselected_text
0f87dea47dbLast session of the day http://twitpic.com/67ezh
196d74cb729exciting
2eee518ae67Recession
301082688c6happy bday!
433987a8ee5I like it!!
5726e501993visitors!
6261932614eHATES
7afa11da83fblocked
8e64208b4efand within a short time of the last clue all…
937bcad24caWhat did you get? My day is alright… haven’t…

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2260653.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

杨振宁大学物理视频中黄色的字,c#写程序去掉(原版改进,三)

上一节&#xff0c;我们分清了主次矛盾&#xff0c;并搞定了主要矛盾&#xff08;去掉黄色的字&#xff09;&#xff0c;这一节解决次要矛盾&#xff08;矩形色带&#xff09;。 我们的想法如图&#xff1a; 1&#xff0c;我们找到稳定黄色的最左边&#xff0c;最右边两点&…

第24周:文献阅读

目录 摘要 Abstract 一、现有问题 二、提出方法 三、创新点 模型结构创新 强化学习与GAN结合 属性特征与通顺性优化 四、方法论 生成对抗网络&#xff08;GAN&#xff09; 强化学习&#xff08;RL&#xff09; 模型组件 五、实验研究 数据集 数据预处理 评价指…

SQL server学习05-查询数据表中的数据(上)

目录 一&#xff0c;基本格式 1&#xff0c;简单的SQL查询语句 2&#xff0c;关键字TOP 3&#xff0c;关键字DISTINCT 二&#xff0c;模糊查询 1&#xff0c;通配符 三&#xff0c;对结果集排序 1&#xff0c;不含关键字DISTINCT 2&#xff0c;含关键字DISTINCT 3&…

【Azure 架构师学习笔记】- Azure Function (1) --环境搭建和背景介绍

本文属于【Azure 架构师学习笔记】系列。 本文属于【Azure Function 】系列。 前言 随着无服务计算的兴起和大数据环境中的数据集成需求&#xff0c; 需要使用某些轻量级的服务&#xff0c;来实现一些简单操作。因此Azure Function就成了微软云上的一个必不可少的组成部分。 …

KeepAlive与RouterView缓存

参考 vue动态组件&#xff1c;Component&#xff1e;与&#xff1c;KeepAlive&#xff1e; KeepAlive官网介绍 缓存之keep-alive的理解和应用 Vue3Vite KeepAlive页面缓存问题 vue多级菜单(路由)导致缓存(keep-alive)失效 vue3 router-view keeperalive对于同一路径但路径…

Linux:进程(环境变量、程序地址空间)

目录 冯诺依曼体系结构 操作系统 设计操作系统的目的 操作系统的管理 进程 PCB fork 进程状态 进程状态查看 僵尸进程 孤儿进程 进程优先级 查看、修改进程优先级命令 竞争、独立、并行、并发 进程切换 活动队列和运行队列 活动队列 过期队列 active指针…

希迪智驾持续亏损8.2亿:毛利率下滑,冲刺“自动驾驶矿卡第一股”

《港湾商业观察》黄懿 近日&#xff0c;希迪智驾&#xff08;湖南&#xff09;股份有限公司&#xff08;下称“希迪智驾”&#xff09;向港交所主板递交上市申请&#xff0c;联席保荐人为中金公司、中信建投国际、中国平安资本&#xff08;香港&#xff09;。 资料显示&#…

Rust之抽空学习系列(三)—— 编程通用概念(中)

Rust之抽空学习系列&#xff08;三&#xff09;—— 编程通用概念&#xff08;中&#xff09; 1、变量&可变性 在Rust中&#xff0c;变量默认是不可变的 fn main() {let x 5;println!("x is {}", x); }使用let来声明一个变量&#xff0c;此时变量默认是不可变…

OpenCV中的识别图片颜色并绘制轮廓

一、实验原理 使用OpenCV库在图像中识别和绘制特定颜色&#xff08;黄色&#xff09;的轮廓 二、实验代码 import cv2 import numpy as np# 读取图片并调整大小 img cv2.imread(./color_1.png) img cv2.resize(img,(600,600))# 将图片从BGR颜色空间转换到HSV颜色空间 img_h…

【Qt】qt基础

目录 一、使用Qt Creator创建qt项目 二、项目文件解析 三、Qt中创建图形化界面的程序的两种方法 四、对象树 五、Qt中处理打印乱码问题的利器&#xff1a;qDebug() 一、使用Qt Creator创建qt项目 1.选择项目模板 选中第一类模板Application(Qt应用程序&#xff0c;包含普…

Transformer入门(6)Transformer编码器的前馈网络、加法和归一化模块

文章目录 7.前馈网络8.加法和归一化组件9.组合所有编码器组件构成完整编码器 7.前馈网络 编码器块中的前馈网络子层如下图所示&#xff1a; 图1.32 – 编码器块 前馈网络由两个带有ReLU激活函数的全连接层组成。全连接层&#xff08;Fully Connected Layer&#xff09;有时也…

AI智算-k8s部署大语言模型管理工具Ollama

文章目录 简介k8s部署OllamaOpen WebUI访问Open-WebUI 简介 Github&#xff1a;https://github.com/ollama/ollama 官网&#xff1a;https://ollama.com/ API&#xff1a;https://github.com/ollama/ollama/blob/main/docs/api.md Ollama 是一个基于 Go 语言开发的可以本地运…

HTML/CSS总结

HTML 1.1 标题标签h 为了使网页更具有语义化&#xff0c;我们经常会在页面中用到标题标签&#xff0c;HTML提供了6个等级的标题&#xff0c;即 标题标签语义&#xff1a; 作为标题使用&#xff0c;并且依据重要性递减 其基本语法格式如下&#xff1a; <h1> 标题文本…

信号处理:概念、技术、领域

目录 基本概念 主要技术 应用领域 信号处理是一个涉及分析、修改和再生信号的多学科领域。信号可以是各种形式的&#xff0c;例如声音、图像、视频或其他类型的监测数据。信号处理的主要目标是提取有用的信息并增强信号的质量。以下是信号处理的一些基本概念和应用&#xff…

黑盒白盒测试

任务1 黑盒测试之等价类划分法 【任务需求】 【问题】例&#xff1a;某报表处理系统要求用户输入处理报表的日期&#xff0c;日期限制在2003年1月至2008年12月&#xff0c;即系统只能对该段期间内的报表进行处理&#xff0c;如日期不在此范围内&#xff0c;则显示输入错误信息…

深度学习物体检测之YOLOV5源码解读

V5比前面版本偏工程化,项目化,更贴合实战 一.V5版本项目配置 (1)整体项目概述 首先github直接查找yolov5&#xff0c;下载下来即可。在训练时&#xff0c;数据是怎么处理的&#xff1f;网络模型架构是怎么设计的(如各层的设计)&#xff1f;yolov5要求是大于python3.8与大于等…

Go 怎么做性能优化芝麻开门篇

一、性能优化的流程 我们在对某个功能&#xff08;或单个接口&#xff09;做性能优化的时候。一般是该功能&#xff08;或接口&#xff09;性能无法满足我们的业务要求&#xff0c;所以被迫优化。在开始优化之前&#xff0c;我们需要明白一些理论知识。 1、常见的性能优化指标…

【Elasticsearch入门到落地】4、Elasticsearch的安装

接上篇《3、es与mysql的概念对比》 上一篇我们学习了Elasticsearch与Mysql的概念与区别。本篇我们来进行Elasticsearch的环境准备及软件安装。 一、环境准备 如果我们没有自己的Linux服务器&#xff0c;且现在正在使用的是Windows操作系统的电脑&#xff0c;那么首先我们需要安…

CRYPTO密码学

加解密算法/编码 哈希算法SM3SHA-3base家族GBGB18030GB2312GBKutf家族恺撒二进制分区法unicodeASCIIDSADSSCRC32校验对称非对称gbk编码h264SEA初探smc动态代码保护四方密码曼彻斯特编码剖析基本概念什么是编码?什么是加密与解密寻找银弹-有没有无法破解的密码通过Java代码入门…

我们来学mysql -- 探讨win安装方式(安装篇)

题记 书接上回&#xff0c;在我们来学mysql – 闲聊(安装篇)中&#xff0c;拿到安装包&#xff0c;当宝贝一样揣在怀里 然而&#xff0c;还没捂热乎&#xff0c;得粉丝秘报&#xff0c;U哥&#xff0c;上篇文章用了滞后的官方文档&#xff0c;哈哈哈…内心的小倔强&#xff0c…