一、背景介绍
1.事件抽取:
根据ACE中的定义,事件由事件触发词(Trigger)和描述事件结构的元素(Argument)构成,元素和事件之间的关系定义为角色(Argument role)。
在后面的任务中,我们首先需要提取出语料库中的每句话,以及它所对应的类别,作为分类任务的训练数据。(暂时没有考虑触发词了)
2.ACE语料(Automatic Content Extraction?)
(1)对事件类型的定义:8个类别,33个子类别
(2)ACE中文语料的来源与层次架构:
(3)apf.xml文件(标注结果)的内容:
其中框起来的“镇压”就是一个触发词,体现了该段的“感情色彩”,也就是该段所对应的“类别”----在第一行的“TYPE”里可以看到是conflict,这也是我们后面做文本分类的label来源。
(4) smg文件(原文)内容:
3.文本分类
(1)传统方法:
(2) 深度学习方法
4.其他tips:
代码规范问题:python代码规范
面向过程-->面向对象编程
二、实战(1.0版 第一次动手实践)
(有小学生的感觉,有些乖蠢笨)
1.构造样本集
这是个繁琐个过程,需要足够的耐心与细心。
首先观察xml文件内容,要看这颗树是如何伸展,在哪里获取我们需要的内容或属性(类似爬虫)
需要的包:
- import re 正则表达式,用来匹配需要解析的文件名
- from lxml import etree 用来解析xml文件
新学了OOP思想,原本我程序写的是完全基于过程(读文件-->由root解析-->提取信息-->转换为想要的格式-->写文件)去思考的,那么现在我可以把提取信息这件事当成一个class,传入的可以是不同的数据(这里也就是语料库不同来源的文件),那么可以为每种(这里是三种bn/nw/wl)定义各自的方法,在main函数调用的时候,分别创建三个instance,去按照各自的方法去提取信息,那么在这当中,读写文件、转换格式的操作都是一样,可以只写一个函数在class里,这样整个class结构就出来啦!所以这样的思想可以帮助我们更宏观地把握程序设计,而不是完全由过程为导向(一环扣一环)虽然执行起来必须是一个接一个,但是思想上可以理解为一种“并行”(我现在是这个感觉,以后可能有更深的体会)。
坑
2.数据预处理(分词、去停用词)
利用哈工大pyltp分词工具,并用停用词表(可自己编辑)
from pyltp import Segmentor
def cut_sentence(file_in):
cutwords_list = [] #清零
file_original_txt = open(file_in,'r',encoding='utf-8')
stopwords = [ line.rstrip() for line in open('stopwords',encoding='utf-8') ] #rstrip() 删除 str末尾的指定字符(默认为空格)
segmentor = Segmentor()
segmentor.load('./project/cws.model') #加载模型
sentences = file_original_txt.readlines()
for sente in sentences:
temp='' #用来存放被切分后的“句子”
sente = str(sente).encode('utf-8').decode('utf-8-sig') #编码、解码。否则label会认为是 '\ufeff' 非法字符
label = str(sente[0:1]) #去label
temp += label+'\t'
sente = sente[2:] #去label
words = segmentor.segment(sente) #分词,类型为 pyltp.VectorOfString
word_list = list(words) #收纳在list中
for word in word_list[1:]:
if word not in stopwords:
temp += word+' '
cutwords_list.append(temp)
segmentor.release() #释放模型
file_original_txt.close()
# with open('f_out.txt','w',encoding='utf-8') as f_out:
# for i in cutwords_list:
# f_out.write(i+'\n')
return cutwords_list
cut_sentence('project/dataset/train_set1.txt')
结果:
3.特征工程(TFIDF)
一步到位的方法:
from sklearn.feature_extraction.text import TfidfVectorizer
vect = TfidfVectorizer(ngram_range=(1,3), min_df=3, max_df=0.9, use_idf=1, smooth_idf=1, sublinear_tf=1)
vect.fit(X_train) # 或者直接X_train = vect.fit_transform(X_train)
X_train = vect.transform(X_train)
X_test = vect.transform(X_test)
我当时的操作:
from sklearn.feature_extraction.text import TfidfTransformer #计算tfidf
from sklearn.feature_extraction.text import CountVectorizer #计算df
vectorizer=CountVectorizer()#该类会将文本中的词语转换为词频矩阵,矩阵元素a[i][j] 表示j词在i类文本下的词频
transformer=TfidfTransformer()#该类会统计每个词语的tf-idf权值
def get_tfidf(file_in):
corpus = []
for sente in cut_sentence(file_in):
corpus.append(sente[2:]) #去label
# print(corpus)
tf=vectorizer.fit_transform(corpus) #第一个fit_transform是将文本转为词频矩阵
tfidf=transformer.fit_transform(tf) #第二个fit_transform是计算tf-idf
print(type(tfidf),'\t',tfidf.shape)
return tfidf
get_tfidf('./project/dataset/train_set0.txt')
结果:
4.分类模型
from sklearn import svm
from sklearn.linear_model.logistic import LogisticRegression
from sklearn.ensemble import RandomForestClassifier
from sklearn.neighbors import KNeighborsClassifier
from sklearn.linear_model import SGDClassifier
from sklearn import tree
from sklearn.externals import joblib
from sklearn import metrics
def training(train_set_in):
X = get_tfidf(train_set_in)
Y = []
for sente in cut_sentence(train_set_in):
Y.append(sente[0:1])
svc=svm.SVC(C=1,kernel='poly',degree=3,gamma=10,coef0=0) #选择模型 & 参数
lr = LogisticRegression()
knn = KNeighborsClassifier()
dt = tree.DecisionTreeClassifier()
rf = RandomForestClassifier()
# multi_target_forest = MultiOutputClassifier(rf) # 构建多输出多分类器
# clf = multi_target_forest.fit(X, Y)
sgdc = SGDClassifier(loss="modified_huber", penalty="l2", alpha=0.00002, n_jobs=-1)# modified_huber; penalty="elasticnet"
# pre = clf.predict(X)
# print('多输出多分类器预测输出分类:\n',y_pred)
clf = sgdc.fit(X,Y) #训练模型
joblib.dump(clf,'train_model.m') #保存模型
def testing(test_set_in):
file_original_txt = open(test_set_in,'r',encoding='utf-8')
original_txt = file_original_txt.readlines() #原始文本
i,j = 0,0
clf = joblib.load('train_model.m') #加载模型
for sente in cut_sentence(test_set_in):
label = int(sente[0:1])
T = []
T.append(str(sente[2:]))
tf=vectorizer.transform(T) #将数据进行转换,比如数据的归一化和标准化,将测试数据按照训练数据同样的模型进行转换,得到特征向量。
tfidf=transformer.transform(tf)
print(label,'\t',int(clf.predict(tfidf)),'\t',str(original_txt[j][2:]))
if label == int(clf.predict(tfidf)):
i += 1
j += 1
print('\nprecision :',i/j)
training('./project/dataset/train_set0.txt')
testing('./project/dataset/test_set0.txt')
结果:
5.模型评估与改进