当前位置:   article > 正文

如何用spaCy训练一个文本分类模型_spacy 训练新模型视频

spacy 训练新模型视频

如何用spaCy训练一个文本分类模型

文本分类是将文本分成不同类别的过程。SpaCy通过textcat组件能够方便地进行自定义文本结构化分类。
文本分类通常用于对影评、酒店服务评论、新闻数据、文本主题等进行分类,或根据投诉类型对客户支持电子邮件进行分类等情况。对于实际案例,训练自定义文本分类模型能够提高分类准确率。本文将向您展示如何使用spaCy库构建自定义文本分类器

本文的案例是基于《火电厂辅机运行规程》中的小标题进行分类的。

  • 目录
  1. 什么是自定义文本分类器模型?
  2. spaCy自定义文本分类入门
  3. 如何准备所需格式的训练数据集?
  4. 如何编写评分函数?
  5. 训练模型并对结果进行评估打分
  6. 用新文本测试模型
  • 先来看一个文本分类的典型用例。

我们在从亚马逊买了一本书之后会填写一份评价反馈。这些评论会帮助商家分析问题并改进服务。我们来看看这个过程:有数百万条评论是由客户填写的,有没有可能手动浏览每一条评论,看看是赞赏还是否定?

当然,不!第一步是将所有审查分为积极和消极两类。然后,你可以很容易地分析有多少人不满意以及为什么不满意。将文本分类为不同 组/标签 的过程称为文本分类。

文本分类的实现方法多种多样。我们将使用spaCy对文本进行分类。spaCy是一个非常流行的NLP库,它提供了最先进(state-of-the-art)的组件。对于实际应用,最好使用经过训练的自定义模型进行分类。下一节将首先介绍什么是定制模型,以及为什么我们需要定制。

##1 什么是自定义文本分类器模型?

假设你有一大堆电影评论/客户评论。你希望把每一个评论都分为正面的或负面的。如果使用默认的spaCy分类器,结果可能不是很好。但是,如果自己收集电影/客户评论的标记数据集,并在此基础上培训自己的模型呢?

结果会更好更准确!你可以通过训练自定义文本分类器来实现。首先在标记好数据集上对它进行训练,并为我们在类似上下文中的使用它做好准备。这非常有用,尤其是在数据量大的情况下,效果更好。

在接下来的部分中,我们将逐步探索如何在spaCy中训练自定义文本分类模型。

2 spaCy自定义文本分类入门

spaCy是一个用于执行NLP任务(如分类)的高级库。spaCy之所以备受青睐,一个重要原因是它允许轻松地构建或扩展文本分类模型。我们将使用这个功能进行文本分类。

接下来,我将用一个真实的例子演示如何训练文本分类器。假设你有文本数据,其中包含文本和分类标记。我们的任务是使用这些数据并训练我们的模型。最后,模型应该能够将一个新的没见过的文本上给出正确的分类标记。

将数据集读入CSV并查看内容。对于我们的任务,我们只需要2列。包含文本和分类标签栏。让我们将这两列提取到一个Dataframe中。

# Import pandas & read csv file
import pandas as pd
cat_set=pd.read_csv("./data/cat_set.csv")

# 提取列,并查看dataframe 
cat_set = cat_set[['TEXT','CAT']]
cat_set.head(10)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
-TEXTCAT
0润滑油净化装置的停运S
1汽动给水泵组事故处理E
2启动锅炉运行调整的主要任务M
3湿式搅拌机的启动R
4一次风机并列O
5引风机启动R
6高压流化风机启动前的检查B
7主机冷油器切换O
8真空系统投运R
9一次风机停止(两台风机运行停止一台)S

CAT标记说明:

标记英文说明中文说明
AAfter启动前
BBefore停运后
CCommon通用规定
EEmergency事故处理
MMaintain运行维护
OOpertation操作
RRun启动
SStop停运

通过上面的数据可以看到每个文本对应相应的标签。我们需要训练自定义模型,让其新的在训练数据中未出现过的文本进行分类。

我们从训练集中得到的原始数据。当我们使用spaCy时,为了训练我们的模型,我们导入spaCy包。导入后,可以加载一个预先训练好的模型,如“zh_core_web_sm”。稍后我们将向这个模型添加/修改文本分类器。对于任何spaCy模型,都可以通过 pipe_names 方法查看当前管道中存在的管道组件。

import spacy
nlp = spacy.load('zh_core_web_sm')
nlp.pipe_names
  • 1
  • 2
  • 3

输出:
[‘tagger’, ‘parser’, ‘ner’]

可以看出它没有文本分类器。所以,我们需要将spaCy的内置textcat管道组件添加到管道中,用于文本分类。使用add_pipe()方法

text_cat=nlp.create_pipe( "textcat", config={"exclusive_classes": True, "architecture": "simple_cnn"})
nlp.add_pipe(text_cat, last=True)
nlp.pipe_names
  • 1
  • 2
  • 3

这样当前管道就变为:

[‘tagger’, ‘parser’, ‘ner’, ‘textcat’]

现在,我们将用我们的数据集训练textcat。

首先,需要向管道组件添加所需的标签。使用add_label函数将这些标签添加到textcat。

for label in ["A","B","C","E","M","O","R","S"]:
	textcat.add_label(label)
  • 1
  • 2

3 如何准备所需格式的训练数据集?

默认textcat模型已经就绪,接下来只需要准备所需格式的数据。

可以编写一个load_data()函数,它将元组列表作为输入。每个元组包含文本和标签值。下面的代码演示了如何将我们的训练数据集转换成所需的格式:

将dataframe转换成一个tuples列表

cat_set['tuples'] = cat_set.apply(lambda row: (row['TEXT'],row['CAT']), axis=1)
train =cat_set['tuples'].tolist()
train[:10]
  • 1
  • 2
  • 3

结果:

[(‘配料’, ‘O’), (‘当锅筒压力到0.3~0.4MPa时,进行如下工作:’, ‘O’), (‘扇区冷却三角手动充水’, ‘O’), (‘转速自动控制方式’, ‘O’), (‘扇区冷却三角程控充水步序:’, ‘O’), (‘尿素溶液输送和储存’, ‘O’), (‘扇区冷却三角程控充水’, ‘O’), (‘油罐油温控制’, ‘O’), (‘空预器事故停机’, ‘O’), (‘小机抽真空与主机同时进行,小机冲转前送轴封:’, ‘O’)]

接下来,您可以将train数据作为输入传递给load_data()函数。

load_data()函数执行以下函数:

  • 使用随机。随机()此函数防止任何基于示例顺序的训练。

  • 对于输入数据中的每个元组,根据标签值指定相应类别,并存储在cats中

  • 80%的输入数据将用于培训,20%用于评估。可以使用split参数更改此比例。

定义函数后,将元组列表传递给上述函数。该函数将返回文本和CAT,用于训练和评估。可以使用load_data()函数来获得最终的训练数据,如下面的代码所示。

def load_data(train, split=0.8):
	import random
	import pandas as pd
	train_data =cat_set['tuples'].tolist()    

	# Shuffle the data
	random.shuffle(train_data)
	texts, labels = zip(*train_data)
	# get the categories for each review
	categories = ["A","B","C","E","M","O","R","S"]
	
	cats = []
	for y in labels:
		cat = {category: 0 for category in categories}
		cat[y] = 1
		cats.append(cat)

	# Splitting the training and evaluation data
	split = int(len(train_data) * split)
	return (texts[:split], cats[:split]), (texts[split:], cats[split:])

# Calling the load_data() function 
(train_texts, train_cats), (dev_texts, dev_cats) = load_data(train)

# Processing the final format of training data
train_data = list(zip(train_texts,[{'cats': cats} for cats in train_cats]))
train_data[:2]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

我们得到最终期望的训练数据格式:

[(‘冲转、升速注意事项:’, {‘cats’: {‘A’: 0, ‘B’: 0, ‘C’: 1, ‘E’: 0, ‘M’: 0, ‘O’: 0, ‘R’: 0, ‘S’: 0}}), (‘炉前燃油系统启动’, {‘cats’: {‘A’: 0, ‘B’: 0, ‘C’: 0, ‘E’: 0, ‘M’: 0, ‘O’: 0, ‘R’: 1, ‘S’: 0}})]

4 如何编写评分函数?

至此,我们已经准备好了所需格式的训练数据,并将其存储在train_data变量中。同时,我们在textcat中得到了模型的文本分类器组件。所以,可以继续在我们的train_data上训练textcat。但是,是不是还少点什么?

对于我们要训练的模型,检查其是否符合我们的预期是很重要的。这一步即是评估模型。这是一个可选的步骤,但强烈建议为选择这一步。load_data()函数会将原始数据的大约20%分割出来进行模型评估。我们将用这个来测试模型的训练效果。

所以,让我们来编写一个函数evaluate(),它可以执行这个评估过程。稍后我们将在模型训练中调用这个evaluate()函数来查看模型的性能。

此函数以textcat和评估数据作为输入。对于评估数据中的每个文本,它从模型预测结果中读取score。代码如下:

def evaluate(tokenizer, textcat, texts, cats):
	docs = (tokenizer(text) for text in texts)
	tp = 0
	for i, doc in enumerate(textcat.pipe(docs)):
		#获取最大值对应的key
		gold = max(cats[i], key=cats[i].get)
		ds =  doc.cats.items()
		h = {}
		[h.update({k:v}) for k,v in ds]
		predict = max(h, key=h.get)
		if gold == predict:
			tp += 1
		
	precision = tp / len(texts)
	return precision
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

5 训练模型

训练数据存储在train_data变量中,组件存储在 texcat 中。

在开始训练之前,需要禁用除 textcat 之外的其他管道组件。这是为了防止其他组件在训练时受到影响。通过disable_pipes()方法完成。接下来,使用begin_training()函数返回优化器。

使用n_iter参数定义在训练数据上迭代模型的次数。在每一次迭代中,我们循环训练数据,并使用spaCy的minibatch和composition helpers将数据划分为多个batch。

spaCy的minibatch()函数将批量返回训练数据。它使用size参数来表示批量大小。您可以使用函数composition()生成size。

函数接受三个输入,分别是start(起始值)、stop(可以生成的最大值)和增长率。size会随着调用次数从start–>stop。

对于每个迭代,通过nlp.update()命令更新。参数如下:

  • docs:使用一个batch文本作为输入。将每个batch传递给zip方法,该方法将返回一个batch的文本和分类。

  • drop:这表示drop out率。

  • losses:字典,保存每个管道组件的损失。创建一个空字典并传递给它。

模型训练完成后,您可以通过调用我们在上一节中定义的evaluate()函数来评估模型所做的预测。

# coding=utf-8

def load_data(split=0.8):
	import random
	import pandas as pd
	cat_set=pd.read_csv("./data/cat_set.csv")
	cat_set['tuples'] = cat_set.apply(lambda row: (row['TEXT'],row['CAT']), axis=1)
	train_data =cat_set['tuples'].tolist()    

	random.shuffle(train_data)
	texts, labels = zip(*train_data)
	# 为每个样本格式化分类字典
	categories = ["A","B","C","E","M","O","R","S"]
	cats = []
	for y in labels:
		cat = {category: 0 for category in categories}
		cat[y] = 1
		cats.append(cat)

	# 划分数据集
	split = int(len(train_data) * split)
	return (texts[:split], cats[:split]), (texts[split:], cats[split:])

# 装载数据 
(train_texts, train_cats), (dev_texts, dev_cats) = load_data()
train_data = list(zip(train_texts,[{'cats': cats} for cats in train_cats]))

from spacy.util import minibatch, compounding
import spacy

nlp = spacy.load('zh_core_web_sm')
text_cat=nlp.create_pipe( "textcat", config={"exclusive_classes": True, "architecture": "simple_cnn"})
nlp.add_pipe(text_cat, last=True)
for label in ["A","B","C","E","M","O","R","S"]:
	text_cat.add_label(label)
	
n_iter = 10
# 禁用其他组件
other_pipes = [pipe for pipe in nlp.pipe_names if pipe != 'textcat']
with nlp.disable_pipes(*other_pipes):  # 只训练 textcat
	optimizer = nlp.begin_training()

	print("Training the model...")
	print('{:^5}\t{:^5}'.format('LOSS', 'PRECISION'))

	# 开始训练
	for i in range(n_iter):
		losses = {}
		batches = minibatch(train_data, size=compounding(4., 32., 1.001))
		for batch in batches:
			texts, annotations = zip(*batch)
			nlp.update(texts, annotations, sgd=optimizer, drop=0.2,
					   losses=losses)
					   
		with text_cat.model.use_params(optimizer.averages):
        score = evaluate(nlp.tokenizer, text_cat, dev_texts, dev_cats)			   

		print('{0:.3f}\t{1:.3f}'.format(losses['textcat'], score))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58

结果如下:

Training the model...
LOSS 	PRECISION
3.979	0.800
1.143	0.832
0.618	0.853
0.424	0.842
0.293	0.863
0.186	0.842
0.226	0.863
0.187	0.842
0.139	0.832
0.101	0.842
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

6 用新文本测试模型

最终这个模型现在可以使用了。

我们来为全新未见过的文本创建一个spaCy doc。它的分类或预测结果将存储在Doc.cats属性。这个Doc.cats属性存储将标签映射到文本类别的评分字典。

#测试模型

texts = ['变频装置操作原则','变频装置送电启动前检查项目','凝泵变频器检修转热备用']
docs = nlp.pipe(texts)
for doc in docs:
	print(doc.text)
	print(doc.cats)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

测试结果:

变频装置操作原则
{'A': 1.4584960581487394e-06, 'B': 4.261386834514269e-07, 'C': 0.9997310042381287, 'E': 1.8897288782682153e-06, 'M': 1.6282596959626972e-07, 'O': 0.00022265917505137622, 'R': 5.221944476829776e-08, 'S': 4.2241339542670175e-05}
变频装置送电启动前检查项目
{'A': 9.86464146990329e-05, 'B': 0.9877699613571167, 'C': 0.009538334794342518, 'E': 0.0017646290361881256, 'M': 1.1881843420269433e-05, 'O': 5.8820318372454494e-05, 'R': 0.0003737462102435529, 'S': 0.0003839542914647609}
凝泵变频器检修转热备用
{'A': 0.568670928478241, 'B': 0.07395660877227783, 'C': 0.0025626164861023426, 'E': 0.21869421005249023, 'M': 0.003099241992458701, 'O': 0.11320023983716965, 'R': 0.0005361264338716865, 'S': 0.019280044361948967}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

这三个句子对应的标记模型给出的结果:‘C’, ‘B’ , ‘A’
我们期望的是:‘C’ , ‘B’ , ‘O’

结语

希望通过上述一个简单的例子,你已经了解了如何使用spaCy训练自定义文本分类模型。使用spaCy训练模型在各个领域都有广泛的应用。与此类似,spaCy还可以训练自定义NER模型,这部分将另开新贴进行论述,敬请关注!

P.s.
很遗憾,这个例子是在spaCy 2.3.0下运行的。如果可能,将来我会将其移植到spaCy V3.0下。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/煮酒与君饮/article/detail/757701
推荐阅读
相关标签
  

闽ICP备14008679号