达观杯文本智能处理挑战赛练习(二)

Task2

学习TF-IDF理论并实践,使用TF-IDF表示文本。

TF-IDF

TF-IDF(Term Frequency-Inverse Document Frequency, 词频-逆文件频率).

TF

词频,就是每个词在文本中出现的次数。
假设现在有一段文本
corpus=[“I come to China to travel”,
“This is a car polupar in China”,
"I love tea and Apple ",
“The work is to write some papers in science”]
不考虑停用词,处理后得到的词向量如下:

[[0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 2 1 0 0]
[0 0 1 1 0 1 1 0 0 1 0 0 0 0 1 0 0 0 0]
[1 1 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0]
[0 0 0 0 0 1 1 0 1 0 1 1 0 1 0 1 0 1 1]]

如果我们直接将统计词频后的19维特征做为文本分类的输入,会发现有一些问题。比如第一个文本,我们发现 “come” , “China” 和 “Travel” 各出现1次,而“to“出现了两次。似乎看起来这个文本与 ”to“ 这个特征更关系紧密。但是实际上”to“是一个非常普遍的词,几乎所有的文本都会用到,因此虽然它的词频为2,但是重要性却比词频为1的 “China” 和 “Travel” 要低的多。如果我们的向量化特征仅仅用词频表示就无法反应这一点。因此我们需要进一步的预处理来反应文本的这个特征,而这个预处理就是TF-IDF。

IDF

IDF即逆文本频率。在上面的例子中可以看到到几乎所有文本都会出现的"to"其词频虽然高,但是重要性却应该比词频低的"China"和“Travel”要低。我们的IDF就是来帮助我们来反应这个词的重要性的,进而修正仅仅用词频表示的词特征值。

概括来讲, IDF反应了一个词在所有文本中出现的频率,如果一个词在很多的文本中出现,那么它的IDF值应该低,比如上文中的“to”。而反过来如果一个词在比较少的文本中出现,那么它的IDF值应该高,说明词条具有很好的类别区分能力。
IDF的公式为:
在这里插入图片描述

TF-IDF

将TF和IDF结合得到TF-IDF
在这里插入图片描述

使用TF-IDF表示文本

用TF-IDF表示达观杯文本比赛的数据

import pickle
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.feature_extraction.text import TfidfVectorizer

data_path='E:/dataset/daguan/new_data/'
#读取数据
train_data=pd.read_csv(data_path+"train_set.csv")
test_data=pd.read_csv(data_path+"test_set.csv")
columns=['article','word_seg']
X_train,X_val,y_train,y_val=train_test_split(train_data[columns],train_data['class'],test_size=0.3, random_state=2019)

vectorizer = TfidfVectorizer()
vectorizer.fit(X_train['word_seg'])
Xtrain=vectorizer.fit_transform(X_train['word_seg'])
Xval=vectorizer.fit_transform(X_train['word_seg'])
print(Xtrain,Xval)
data=(Xtrain,Xval,y_train,y_val)
#保存到本地
fp = open('./feat/data_tfidf.pickl', 'wb')
pickle.dump(data, fp)
fp.close()

使用tf-idf表示的数据:
在这里插入图片描述

Reference

https://www.cnblogs.com/pinard/p/6693230.html
https://github.com/Heitao5200/DGB/blob/master/feature/feature_code/tfidf.py

Task3

one-hot表示

传统的基于规则或基于统计的自然语义处理方法将单词看作一个原子符号
被称作one-hot representation。one-hot representation把每个词表示为一个长向量。这个向量的维度是词表大小,向量中只有一个维度的值为1,其余维度为0,这个维度就代表了当前的词。
例如: 苹果 [0,0,0,1,0,0,0,0,0,……]
不足:难以发现词之间的关系,以及难以捕捉句法(结构)和语义(意思)之间的关系

分布式表示(distribution representation)

word embedding指的是将词转化成一种分布式表示,又称词向量。分布式表示将词表示成一个定长的连续的稠密向量。这种向量一般长成这个样子:[0.792, −0.177, −0.107, 0.109, −0.542, …]。
分布式表示优点:
(1)词之间存在相似关系:
是词之间存在“距离”概念,这对很多自然语言处理的任务非常有帮助。
(2)包含更多信息:
词向量能够包含更多信息,并且每一维都有特定的含义。在采用one-hot特征时,可以对特征向量进行删减,词向量则不能。
Dristributed representation可以解决One hot representation的问题,它的思路是通过训练,将每个词都映射到一个较短的词向量上来。所有的这些词向量就构成了向量空间,进而可以用普通的统计学的方法来研究词与词之间的关系。这个较短的词向量维度是多大呢?这个一般需要我们在训练时自己来指定。
比如下图我们将词汇表里的词用"Royalty",“Masculinity”, "Femininity"和"Age"4个维度来表示,King这个词对应的词向量可能是(0.99,0.99,0.05,0.7)(0.99,0.99,0.05,0.7)。当然在实际情况中,我们并不能对词向量的每个维度做一个很好的解释。

在这里插入图片描述
我们将king这个词从一个可能非常稀疏的向量坐在的空间,映射到现在这个四维向量所在的空间,必须满足以下性质:
(1)这个映射是单设);
(2)映射之后的向量不会丢失之前的那种向量所含的信息。
这个过程称为word embedding(词嵌入),即将高维词向量嵌入到一个低维空间。
在这里插入图片描述

word2vec

word2vec模型其实就是简单化的神经网络。
在这里插入图片描述
输入是One-Hot Vector,Hidden Layer没有激活函数,也就是线性的单元。Output Layer维度跟Input Layer的维度一样,用的是Softmax回归。当这个模型训练好以后,我们并不会用这个训练好的模型处理新的任务,我们真正需要的是这个模型通过训练数据所学得的参数,例如隐层的权重矩阵。

这个模型是如何定义数据的输入和输出呢?一般分为CBOW(Continuous Bag-of-Words 与Skip-Gram两种模型。CBOW模型的训练输入是某一个特征词的上下文相关的词对应的词向量,而输出就是这特定的一个词的词向量。 Skip-Gram模型和CBOW的思路是反着来的,即输入是特定的一个词的词向量,而输出是特定词对应的上下文词向量。CBOW对小型数据库比较合适,而Skip-Gram在大型语料中表现更好。
CBOW模型
CBOW模型
在这里插入图片描述
Skip-Gram模型

实践

import pandas as pd
import gensim
import time
import pickle
import numpy as np
import csv,sys
vector_size = 100

maxInt = sys.maxsize
decrement = True
while decrement:
    # decrease the maxInt value by factor 10
    # as long as the OverflowError occurs.
    decrement = False
    try:
        csv.field_size_limit(maxInt)
    except OverflowError:
        maxInt = int(maxInt/10)
        decrement = True

#=======================================================================================================================
# 0 辅助函数
#=======================================================================================================================

def sentence2list(sentence):
    return sentence.strip().split()

start_time = time.time()

data_path = 'E:/MyPython/机器学习——达观杯/data_set/'
feature_path = 'E:/MyPython/机器学习——达观杯/feature/feature_file/'
proba_path = 'E:/MyPython/机器学习——达观杯/proba/proba_file/'
model_path = 'E:/MyPython/机器学习——达观杯/model/model_file/'
result_path ="E:/MyPython/机器学习——达观杯/result/"
#=======================================================================================================================
# 1 准备训练数据
#=======================================================================================================================

print("准备数据................ ")
df_train = pd.read_csv(data_path +'train_set1.csv',engine='python')
df_test = pd.read_csv(data_path +'test_set1.csv',engine='python')
sentences_train = list(df_train.loc[:, 'word_seg'].apply(sentence2list))
sentences_test = list(df_test.loc[:, 'word_seg'].apply(sentence2list))
sentences = sentences_train + sentences_test
print("准备数据完成! ")

#=======================================================================================================================
# 2 训练
#=======================================================================================================================
print("开始训练................ ")
model = gensim.models.Word2Vec(sentences=sentences, size=vector_size, window=5, min_count=5, workers=8, sg=0, iter=5)
print("训练完成! ")

#=======================================================================================================================
# 3 提取词汇表及vectors,并保存
#=======================================================================================================================
print(" 保存训练结果........... ")
wv = model.wv
vocab_list = wv.index2word
word_idx_dict = {}
for idx, word in enumerate(vocab_list):
    word_idx_dict[word] = idx
    
vectors_arr = wv.vectors
vectors_arr = np.concatenate((np.zeros(vector_size)[np.newaxis, :], vectors_arr), axis=0)#第0位置的vector为'unk'的vector

f_wordidx = open(feature_path + 'word_seg_word_idx_dict.pkl', 'wb')
f_vectors = open(feature_path + 'word_seg_vectors_arr.pkl', 'wb')
pickle.dump(word_idx_dict, f_wordidx)
pickle.dump(vectors_arr, f_vectors)
f_wordidx.close()
f_vectors.close()
print("训练结果已保存到该目录下! ")

end_time = time.time()
print("耗时:{}s ".format(end_time - start_time))

Task4

LR模型预测

逻辑回归

如果我们忽略二分类问题中y的取值是一个离散的取值(0或1),我们继续使用线性回归来预测y的取值。这样做会导致y的取值并不为0或1。逻辑回归使用一个函数来归一化y值,使y的取值在区间(0,1)内,这个函数称为Logistic函数(logistic function),也称为Sigmoid函数(sigmoid function)。函数公式如下:
在这里插入图片描述
Logistic函数当z趋近于无穷大时,g(z)趋近于1;当z趋近于无穷小时,g(z)趋近于0。Logistic函数的图形如下:
在这里插入图片描述
逻辑回归本质上是线性回归,只是在特征到结果的映射中加入了一层函数映射,即先把特征线性求和,然后使用函数g(z)作为最终假设函数来预测。g(z)可以将连续值映射到0到1之间。线性回归模型的表达式带入g(z),就得到逻辑回归的表达式:
在这里插入图片描述

用LR模型预测

from sklearn.model_selection import GridSearchCV
import pickle
import pandas as pd
from sklearn.linear_model import LogisticRegression
from sklearn.externals import joblib
from sklearn.metrics import roc_auc_score,f1_score,accuracy_score
from sklearn.model_selection import GridSearchCV
from sklearn import svm
from sklearn.model_selection import train_test_split

feat_path="D:/Code/NLPPractice/feat/"
model_path="D:/Code/NLPPractice/model/"
result_path="D:/Code/NLPPractice/result/"
#读取特征
data_fp=open(feat_path + "data_w_tfidf.pkl", 'rb')
x_train, y_train, x_test = pickle.load(data_fp)
xTrain, xVal, yTrain, yVal = train_test_split(x_train, y_train, test_size=0.30, random_state=531)
'''
#模型训练
lr = LogisticRegression(C=120,dual=True)
lr.fit(xTrain,yTrain)

#保存模型
joblib.dump(lr, model_path + "LR(120)_data_w_tfidf.m")
'''
lr=joblib.load(model_path+"LR(120)_data_w_tfidf.m")

#预测结果
predicts = lr.predict(xVal)
accuracy=accuracy_score(yVal,predicts)

print('Accuracy :%2f%%'% (accuracy*100.0))

f1score=f1_score(yVal,predicts,average='micro')
print('F1_score :%f1'% f1score)

在这里插入图片描述
准确率:78.58%,F1评分为0.78,本来想使用gridsearchcv调参的,但是搞了半天,好像不能对LR多分类模型进行调参,也可能是我没找到,以前只对二分类调过参。

SVM模型预测

SVM

SVM中最关键的思想之一就是引入和定义了“间隔”这个概念。这个概念本身很简单,以二维空间为例,就是点到分类直线之间的距离。假设直线为y=wx+b,那么只要使所有正分类点到该直线的距离与所有负分类点到该直线的距离的总和达到最大,这条直线就是最优分类直线。这样,原问题就转化为一个约束优化问题,可以直接求解。这叫做硬间隔最大化,得到的SVM模型称作硬间隔支持向量机。
但是新问题出现了,在实际应用中,我们得到的数据并不总是完美的线性可分的,其中可能会有个别噪声点,他们错误的被分类到了其他类中。如果将这些特异的噪点去除后,可以很容易的线性可分。但是,我们对于数据集中哪些是噪声点却是不知道的,如果以之前的方法进行求解,会无法进行线性分开。是不是就没办法了呢?假设在y=x+1直线上下分为两类,若两类中各有对方的几个噪点,在人的眼中,仍然是可以将两类分开的。这是因为在人脑中是可以容忍一定的误差的,仍然使用y=x+1直线分类,可以在最小误差的情况下进行最优的分类。同样的道理,我们在SVM中引入误差的概念,将其称作“松弛变量”。通过加入松弛变量,在原距离函数中需要加入新的松弛变量带来的误差,这样,最终的优化目标函数变成了两个部分组成:距离函数和松弛变量误差。这两个部分的重要程度并不是相等的,而是需要依据具体问题而定的,因此,我们加入权重参数C,将其与目标函数中的松弛变量误差相乘,这样,就可以通过调整C来对二者的系数进行调和。如果我们能够容忍噪声,那就把C调小,让他的权重降下来,从而变得不重要;反之,我们需要很严格的噪声小的模型,则将C调大一点,权重提升上去,变得更加重要。通过对参数C的调整,可以对模型进行控制。这叫做软间隔最大化,得到的SVM称作软间隔支持向量机。
之前的硬间隔支持向量机和软间隔支持向量机都是解决线性可分数据集或近似线性可分数据集的问题的。但是如果噪点很多,甚至会造成数据变成了线性不可分的,那该怎么办?最常见的例子是在二维平面笛卡尔坐标系下,以原点(0,0)为圆心,以1为半径画圆,则圆内的点和圆外的点在二维空间中是肯定无法线性分开的。但是,学过初中几何就知道,对于圆圈内(含圆圈)的点:x2+y2≤1,圆圈外的则x2+y2>1。我们假设第三个维度:z=x2+y2,那么在第三维空间中,可以通过z是否大于1来判断该点是否在圆内还是圆外。这样,在二维空间中线性不可分的数据在第三维空间很容易的线性可分了。这就是非线性支持向量机。

使用svm进行预测

from sklearn.model_selection import GridSearchCV
import pickle
import pandas as pd
from sklearn.linear_model import LogisticRegression
from sklearn.externals import joblib
from sklearn.metrics import roc_auc_score,f1_score,accuracy_score
from sklearn.model_selection import GridSearchCV
from sklearn import svm
from sklearn.model_selection import train_test_split

feat_path="D:/Code/NLPPractice/feat/"
model_path="D:/Code/NLPPractice/model/"
result_path="D:/Code/NLPPractice/result/"
#读取特征
data_fp=open(feat_path + "data_w_tfidf.pkl", 'rb')
x_train, y_train, x_test = pickle.load(data_fp)
xTrain, xVal, yTrain, yVal = train_test_split(x_train, y_train, test_size=0.30, random_state=531)
'''

#模型训练
clf = svm.LinearSVC(C=5,dual=False)
clf.fit(xTrain,yTrain)

#保存模型
joblib.dump(clf, model_path + "SVM(c5)_data_w_tfidf.m")
'''


clf=joblib.load(model_path+"SVM(c5)_data_w_tfidf.m")

#预测结果
predicts = clf.predict(xVal)
print(predicts)
accuracy=accuracy_score(yVal,predicts)

print('Accuracy :%2f%%'% (accuracy*100.0))

f1score=f1_score(yVal,predicts,average='micro')
print('F1_score :%f1'% f1score)

在这里插入图片描述
准确率:78.78%,F1评分为0.79

Reference

https://www.cnblogs.com/zhizhan/p/4430253.html
https://github.com/Heitao5200/DGB/blob/master/model/model_code/SVM_data_w_tfidf.py

Task5

LightGBM

from sklearn.model_selection import GridSearchCV
import pickle
import pandas as pd
from sklearn.externals import joblib
from sklearn.metrics import roc_auc_score,f1_score,accuracy_score
from sklearn.model_selection import GridSearchCV
from sklearn.model_selection import train_test_split
import lightgbm as LGB
import numpy as np

feat_path="D:/Code/NLPPractice/feat/"
model_path="D:/Code/NLPPractice/model/"
result_path="D:/Code/NLPPractice/result/"

#自定义验证集的评价函数
def f1_score_vali(preds, data_vali):
    labels = data_vali.get_label()
    preds = np.argmax(preds.reshape(19, -1), axis=0)
    score_vali = f1_score(y_true=labels, y_pred=preds, average='macro')
    return 'f1_score', score_vali, True

#读取特征
data_fp=open(feat_path + "data_w_tfidf.pkl", 'rb')
x_train, y_train, x_test = pickle.load(data_fp)
xTrain, xVal, yTrain, yVal = train_test_split(x_train, y_train, test_size=0.30, random_state=531)

d_train = LGB.Dataset(data=xTrain, label=yTrain)
d_vali = LGB.Dataset(data=xVal, label=yVal)
#构建模型
params = {
    'boosting': 'gbdt',
    'application': 'multiclassova',
    'num_class': 19,
    'learning_rate': 0.1,
    'num_leaves': 31,
    'max_depth': -1,
    'lambda_l1': 0,
    'lambda_l2': 0.5,
    'bagging_fraction': 1.0,

}
'''
bst = LGB.train(params, d_train, num_boost_round=800, valid_sets=d_vali, feval=f1_score_vali,
                early_stopping_rounds=None,
                verbose_eval=True)

joblib.dump(bst, model_path + "LGB_data_w_tfidf.m")
'''
#bst=joblib.load(model_path+"LGB_data_w_tfidf.m")
y_proba = bst.predict(xVal)
predicts = np.argmax(y_proba.reshape(19, -1), axis=0)
print(len(predicts),len(yVal))
accuracy=accuracy_score(yVal,predicts)

print('Accuracy :%2f%%'% (accuracy*100.0))

f1score=f1_score(yVal,predicts,average='micro')
print('F1_score :%f1'% f1score)

这个f1score和accuracy都很低,我觉得应该是轮数太少的原因,我电脑配置太差,跑下来太慢了,用gridsearchcv调参的话,应该更慢,过两天有时间再回来找下原因。

Reference

https://github.com/Heitao5200/DGB/blob/master/model/model_code/LGB_data_w_tfidf.py

Task6

模型融合

import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn import svm
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score
from sklearn.linear_model import LogisticRegression
import lightgbm as lgb
from sklearn.model_selection import GridSearchCV
import gensim
import time
import pickle
import csv,sys

# read data
df = pd.read_csv('data/train_set.csv', nrows=5000)
df.drop(columns='article', inplace=True)

# observe data
# print(df['class'].value_counts(normalize=True, ascending=False))

# TF-IDF
vectorizer = TfidfVectorizer(ngram_range=(1, 2), min_df=3, max_df=0.9, sublinear_tf=True)
vectorizer.fit(df['word_seg'])
x_train = vectorizer.transform(df['word_seg'])

# split training set and validation set
predictor = ['word_seg']
x_train, x_validation, y_train, y_validation = train_test_split(x_train, df['class'], test_size=0.2)


clf = LogisticRegression(C=10, max_iter=20)
clf = svm.LinearSVC(C=1, max_iter=20)
clf = lgb.sklearn.LGBMClassifier(learning_rate=0.1, n_estimators=50, num_leaves=10)

algorithms=[
LogisticRegression(C=10, max_iter=20),
svm.LinearSVC(C=1, max_iter=20),
]
full_predictions = []
for alg in algorithms:
    # Fit the algorithm using the full training data.
    alg.fit(x_train, y_train)
    # Predict using the test dataset.  We have to convert all the columns to floats to avoid an error.
    predictions = alg.decision_function(x_validation.astype(float))
    full_predictions.append(predictions)


y_prediction = (full_predictions[0] + full_predictions[1]) / 2

# adjust labels from 1 to 19
y_prediction = np.argmax(y_prediction, axis=1)+1


# # grid search for model
# param_grid = {
#     'num_leaves': [10, 20, 30],
#     'learning_rate': [0.01, 0.05, 0.1],
#     'n_estimators': [10, 20, 50]
# }
# gbm = GridSearchCV(clf, param_grid, cv=5, scoring='f1_micro', n_jobs=4, verbose=1)
# gbm.fit(x_train, y_train)
# print('网格搜索得到的最优参数是:', gbm.best_params_)

# test model
label = []
for i in range(1, 20):
    label.append(i)
f1 = f1_score(y_validation, y_prediction, labels=label, average='micro')
print('The F1 Score: ' + str("%.4f" % f1))
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值