文本分类任务的基础实现(一)——机器学习部分_特征工程_代码分解

11 篇文章 2 订阅
10 篇文章 14 订阅

该部分用于文本分类任务的基础实现,主要包括机器学习(ml)和深度学习(dl)两大部分,机器学习部分基于sklearn/lightgbm包实现,深度学习部使用pytorch深度学习框架。

机器学习部分主要包含特征工程分类器两大部分:

  • 特征工程部分主要针对文本分类任务的hash/lsa/lda/doc2vec特征提取/特征选择/特征组合/特征构造进行了实现;

  • 分类器部分主要有逻辑回归/SVM/随机森林/Bagging/Adaboost/GBDT /Xgboost /LightGBM等,此部分下一博客介绍。

深度学习主要实现了word2vec/构建lstm模型/训练可视化等。(注:只是基础实现,并不是最优)

数据链接:https://pan.baidu.com/s/17UjEEcB2taT_HvU1FC1bCQ
代码工程链接:https://github.com/MLjian/TextClassificationImplement
代码分解如下:

特征工程

特征提取

1. 数据预处理

import pandas as pd
import pickle

df_train = pd.read_csv('../data/train_set.csv')
df_train.drop(columns='article', inplace=True)
df_test = pd.read_csv('../data/test_set.csv')
df_test.drop(columns='article', inplace=True)
f_all = pd.concat(objs=[df_train, df_test], axis=0, sort=True)
y_train = (df_train['class'] - 1).values

2. 特征提取

2.1 tf特征
  • 将原始数据数字化为tf特征,并将结果保存至本地
from sklearn.feature_extraction.text import CountVectorizer

vectorizer = CountVectorizer(ngram_range=(1, 2), min_df=100, max_df=0.8)
vectorizer.fit(df_all['word_seg'])
x_train = vectorizer.transform(df_train['word_seg'])
x_test = vectorizer.transform(df_test['word_seg'])
2.2 tfidf特征
  • 将原始数据数字化为tfidf特征,并将结果保存至本地
from sklearn.feature_extraction.text import TfidfVectorizer

vectorizer = TfidfVectorizer(ngram_range=(1, 2), min_df=3, max_df=0.9, sublinear_tf=True)
vectorizer.fit(df_train['word_seg'])
x_train = vectorizer.transform(df_train['word_seg'])
x_test = vectorizer.transform(df_test['word_seg'])
2.3 hash特征
  • 将原始数据数字化为hash特征
from sklearn.feature_extraction.text import HashingVectorizer

vectorizer = HashingVectorizer(ngram_range=(1, 2), n_features=200)
d_all = vectorizer.fit_transform(df_all['word_seg'])
x_train = d_all[:len(y_train)]
x_test = d_all[len(y_train):]
2.4 word2vec特征
  • 将原始数据数字化为doc2vec特征,并将结果保存至本地
from gensim.models.doc2vec import Doc2Vec, TaggedDocument 

"""=====================================================================================================================
0 辅助函数 
"""
def sentence2list(sentence):
    s_list = sentence.strip().split()
    return s_list

"""====================================================================
1 读取原始数据,并进行简单处理
"""
df_train = pd.read_csv('../data/train_set.csv')
df_train.drop(columns='article', inplace=True)
df_test = pd.read_csv('../data/test_set.csv')
df_test.drop(columns='article', inplace=True)
df_all = pd.concat(objs=[df_train, df_test], axis=0, sort=True)
y_train = (df_train['class'] - 1).values

df_all['word_list'] = df_all['word_seg'].apply(sentence2list)
texts = df_all['word_list'].tolist()

"""====================================================================
2 doc2vec
"""
documents = [TaggedDocument(doc, [i]) for i, doc in enumerate(texts)]
model = Doc2Vec(documents, vector_size=200, window=5, min_count=3, workers=4, epochs=25)
docvecs = model.docvecs

x_train = []
for i in range(0, 102277):
    x_train.append(docvecs[i])
x_train = np.array(x_train)

x_test = []
for j in range(102277, 204554):
    x_test.append(docvecs[j])
x_test = np.array(x_test)
2.5 Isa特征
  • 将tfidf特征降维为lsa特征
from sklearn.decomposition import TruncatedSVD

"""读取tfidf特征"""
tfidf_path = './data_tfidf_selected_lsvc_l2_143w.pkl'
f_tfidf = open(tfidf_path, 'rb')
x_train, y_train, x_test = pickle.load(f_tfidf)
f_tfidf.close()

"""特征降维:lsa"""
print("lsa......")
lsa = TruncatedSVD(n_components=200)
x_train = lsa.fit_transform(x_train)
x_test = lsa.transform(x_test)
2.6 Ida特征
  • 将tf特征降维为lda特征,并将结果保存至本地
1 tf特征加载

tf_path = './tf_select_LSVC_l2644235.pkl'
f_tf = open(tf_path, 'rb')
x_train, y_train, x_test = pickle.load(f_tf)
f_tf.close()

2 特征降维:lda

print("lda......")
lda = LatentDirichletAllocation(n_components=200)
x_train = lda.fit_transform(x_train)
x_test = lda.transform(x_test)

3. 提取的特征同代码保存到本地(命名不同)

data = (x_train, y_train, x_test)
fp =open('./各方法产生的特征存放命名.pkl', 'wb')
pickle.dump(data, fp)
fp.close()

附:

pickle.dump()和pickle.load()方法

pickle.dump( obj, file, [,protocol] )

详解:序列化对象。将对象obj保存到文件file中,参数protocol是序列化模式,默认为0(ASCII协议,表示以文本的形式进行序列化),protocol可为1(表示老式二进制协议),2(表示新式二进制协议)。file表示保存到类文件对象,file必须有write()接口,file可以是以‘w’打开的文件或者一个stringIO对象,也可以是任何可以实现write()接口的对象。

pickle.load()

详解:反序列化对象。将文件中的数据解析为一个python对象,file有read()接口和readline()接口。

特征选择

  • 对特征进行嵌入式选择
1. 读取特征
# -*- coding: utf-8 -*-
import time
import pickle
from sklearn.feature_selection import SelectFromModel
from sklearn.svm import LinearSVC

t_start = time.time()
features_path = './data_tfidf_select_LSVC_l2_901288_select_LSVC_l2_279950.pkl'#tfidf特征的路径
fp = open(features_path, 'rb')
x_train, y_train, x_test = pickle.load(fp)
fp.close()
2. 进行特征选择
alo_name = 'LSVC_l2' #保存模型名字用的
lsvc = LinearSVC(penalty='l2', C=1.0, dual=True).fit(x_train, y_train)
# LinearSVC是使用L1正则化的线性模型,其L1正则化可生成一个稀疏矩阵,利于计算,所以可以做特征选择。
slt = SelectFromModel(lsvc, prefit=True)
x_train_s = slt.transform(x_train)
x_test_s = slt.transform(x_test)
SelectFromModel

sklearn在Feature selection模块中内置了一个SelectFromModel,该模型可以通过Model本身给出的指标对特征进行选择,其作用与其名字高度一致,select (feature) from model。
SelectFromModel 是一个通用转换器,其需要的Model只需要带有conef_或者feature_importances属性,那么就可以作为SelectFromModel的Model来使用. 如果相关的coef_ 或者 featureimportances 属性值低于预先设置的阈值,这些特征将会被认为不重要并且移除掉。除了指定数值上的阈值之外,还可以通过给定字符串参数来使用内置的启发式方法找到一个合适的阈值。可以使用的启发式方法有 mean 、 median 以及使用浮点数乘以这些(例如,0.1*mean )。

根据基础学习的不同,在estimator中有两种选择方式
  • 第一种是基于L1的特征选择,使用L1正则化的线性模型会得到稀疏解,当目标是降低维度的时候,可以使用sklearn中的给予L1正则化的线性模型,比如LinearSVC,逻辑回归,或者Lasso。但是要注意的是:在 SVM 和逻辑回归中,参数 C 是用来控制稀疏性的:小的 C 会导致少的特征被选择。使用 Lasso,alpha 的值越大,越少的特征会被选择。

  • 第二种是给予Tree的特征选择,Tree类的算法包括决策树,随机森林等会在训练后,得出不同特征的重要程度,我们也可以利用这一重要属性对特征进行选择。

但是无论选择哪一种学习器,我们都要记住的是我们的特征选择的最终标准应当是选择最好的特征,而非必须依照某种方法进行选择。

几个重要的参数,属性,方法
  • threshold : 阈值,string, float, optional default None
    • 可以使用:median 或者 mean 或者 1.25 * mean 这种格式。
    • 如果使用参数惩罚设置为L1,则使用的阈值为1e-5,否则默认使用用mean
  • prefit :布尔,默认为False,是否为训练完的模型,(注意不能是cv,- GridSearchCV或者clone the estimator得到的),如果是False的话则先fit,再transform。

  • threshold_ :采用的阈值

SelectFromModel详见:
https://www.baidu.com/link?url=yo5xZHAfolt5hThaa1SlU6yOdzc7OMk1BzDif3YOPU_jkxrWSngQIOuI3ntjBiCVo0RniSDbH7DZJvS0d4JiOD85I7EOrZeXMDvivkB3K9C&wd=&eqid=c085d258000398c2000000045b9b7aef

svc使用代码示例:

<span style="font-family:Microsoft YaHei;"><span style="font-family:Microsoft YaHei;font-size:14px;">'''
SVC参数解释
(1)C: 目标函数的惩罚系数C,用来平衡分类间隔margin和错分样本的,default C = 1.0;
(2)kernel:参数选择有RBF, Linear, Poly, Sigmoid, 默认的是"RBF";
(3)degree:if you choose 'Poly' in param 2, this is effective, degree决定了多项式的最高次幂;
(4)gamma:核函数的系数('Poly', 'RBF' and 'Sigmoid'), 默认是gamma = 1 / n_features;
(5)coef0:核函数中的独立项,'RBF' and 'Poly'有效;
(6)probablity: 可能性估计是否使用(true or false);
(7)shrinking:是否进行启发式;
(8)tol(default = 1e - 3): svm结束标准的精度;
(9)cache_size: 制定训练所需要的内存(以MB为单位);
(10)class_weight: 每个类所占据的权重,不同的类设置不同的惩罚参数C, 缺省的话自适应;
(11)verbose: 跟多线程有关,不大明白啥意思具体;
(12)max_iter: 最大迭代次数,default = 1if max_iter = -1, no limited;
(13)decision_function_shape : ‘ovo’ 一对一, ‘ovr’ 多对多  or None 无, default=None
(14)random_state :用于概率估计的数据重排时的伪随机数生成器的种子。
 ps:7,8,9一般不考虑。
'''
from sklearn.svm import SVC
import numpy as np
X= np.array([[-1,-1],[-2,-1],[1,1],[2,1]])
y = np.array([1,1,2,2])

clf = SVC()
clf.fit(X,y)
print clf.fit(X,y)
print clf.predict([[-0.8,-1]])</span></span>

第一个打印出的是svc训练函数的参数,其更多参数说明请参考:点击阅读
最后一行打印的是预测结果

这里写图片描述

3. 保存选择后的特征至本地
num_features = x_train_s.shape[1]
data_path = './' + features_path.split('.')[-2] + '_select_' + alo_name + '_' + str(num_features) + '.pkl'
data_f = open(data_path, 'wb') 
pickle.dump((x_train_s, y_train, x_test_s), data_f)
data_f.close()

t_end = time.time()
print("特征选择完成,选择{}个特征,共耗时{}min".format(num_features, (t_end-t_start)/60))

特征组(融)合

  • 法一:lda/lsa/doc2vec三种特征进行特征融合,并将结果保存至本地
1. 读取lda/lsa/doc2vec特征
import numpy as np
import pickle
import time
t_start = time.time()

f1 = open('./data_lda.pkl', 'rb')
x_train_1, y_train, x_test_1 = pickle.load(f1)
f1.close()

f2 = open('./data_s_lsvc_l2_143w_lsa.pkl', 'rb')
x_train_2, y_train, x_test_2 = pickle.load(f2)
f2.close()

f3 = open('./data_doc2vec_25.pkl', 'rb')
x_train_3, _, x_test_3 = pickle.load(f3)
f3.close()
2. 对这三种特征进行拼接融合
x_train = np.concatenate((x_train_1, x_train_2, x_train_3), axis=1)
x_test = np.concatenate((x_test_1, x_test_2, x_test_3), axis=1)
3. 将融合后的特征,保存至本地
data = (x_train, y_train, x_test)
fp = open('./data_ensemble.pkl', 'wb')
pickle.dump(data, fp)
fp.close()

t_end = time.time()
print("已将原始数据数字化为融合的特征,共耗时:{}min".format((t_end-t_start)/60))
  • 法二:将data_ensemble(上面融合的特征)特征转换为稀疏矩阵,并将其合并到tfidf
1. 读取ensemble特征
import pickle
from scipy import sparse
from scipy.sparse import hstack


f_ensemble = open('./data_ensemble.pkl', 'rb')
x_train_ens, y_train, x_test_ens = pickle.load(f_ensemble)
f_ensemble.close()
2. 将data_ensemble特征转换为稀疏矩阵,并将其合并到tfidf
"""将numpy 数组 转换为 csr稀疏矩阵"""
x_train_ens_s = sparse.csr_matrix(x_train_ens)
x_test_ens_s = sparse.csc_matrix(x_test_ens)

"""读取tfidf特征"""
f_tfidf = open('./data_tfidf_select_LSVC_l2_17107.pkl', 'rb')
x_train_tfidf, _, x_test_tfidf = pickle.load(f_tfidf)
f_tfidf.close()

"""对两个稀疏矩阵进行合并"""
x_train_spar = hstack([x_train_ens_s, x_train_tfidf])
x_test_spar = hstack([x_test_ens_s, x_test_tfidf])
3. 将合并后的稀疏特征保存至本地
data = (x_train_spar, y_train, x_test_spar)
f = open('./data_ensemble_spar.pkl', 'wb')
pickle.dump(data, f)
f.close()

特征构造

  • 根据已有的特征,使用多项式方法构造出更多特征
1. 读取原特征
# -*- coding: utf-8 -*-
import pickle
import time
from sklearn.preprocessing import PolynomialFeatures

t_start = time.time()

features_path = './data_s_lsvc_l2_143w_lsa.pkl'
f = open(features_path, 'rb')
x_train, y_train, x_test = pickle.load(f)
f.close()
2. 使用多项式方法构造出更多的特征
poly = PolynomialFeatures(degree=2, interaction_only=True, include_bias=False)#degree控制多项式最高次数
x_train_new = poly.fit_transform(x_train)
x_test_new = poly.transform(x_test)
PolynomialFeatures类

使用sklearn.preprocessing.PolynomialFeatures来进行特征的构造。

它是使用多项式的方法来进行的,如果有a,b两个特征,那么它的2次多项式为(1,a,b,a^2,ab, b^2),这个多项式的形式是使用poly的效果。
PolynomialFeatures有三个参数:

  • degree:控制多项式的度;

  • interaction_only: 默认为False,如果指定为True,就不会有特征自己和自己结合的项,即上面的二次项中没有a^2和b^2。

  • include_bias:默认为True。如果为True的话,那么就会有上面的 1那一项。

3. 将构造好的特征保存至本地
data = (x_train_new, y_train,  x_test_new)
features_constr_path = features_path.split('/')[-1] + '_constr.pkl'
f_data = open(features_constr_path, 'wb')
pickle.dump(data, f_data)
f_data.close()

t_end = time.time()
print("构造特征完成,共耗时:{}min".format((t_end-t_start)/60))
  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值