分类目录归档:机器学习

AutoGrad 这个Python神器能够帮你自动计算函数斜率和梯度

AutoGrad 是一个老少皆宜的 Python 梯度计算模块。

对于初高中生而言,它可以用来轻易计算一条曲线在任意一个点上的斜率。

对于大学生、机器学习爱好者而言,你只需要传递给它Numpy这样的标准数据库下编写的损失函数,它就可以自动计算损失函数的导数(梯度)。

我们将从普通斜率计算开始,介绍到如何只使用它来实现一个逻辑回归模型。

1.准备

开始之前,你要确保Python和pip已经成功安装在电脑上,如果没有,请访问这篇文章:超详细Python安装指南 进行安装。

(可选1) 如果你用Python的目的是数据分析,可以直接安装Anaconda:Python数据分析与挖掘好帮手—Anaconda,它内置了Python和pip.

(可选2) 此外,推荐大家用VSCode编辑器来编写小型Python项目:Python 编程的最好搭档—VSCode 详细指南

Windows环境下打开Cmd(开始—运行—CMD),苹果系统环境下请打开Terminal(command+空格输入Terminal),输入命令安装依赖:

pip install autograd

2.AutoGrad 计算斜率

对于初高中生同学而言,它可以用来轻松计算斜率,比如我编写一个斜率为0.5的直线函数:

# 公众号 Python实用宝典
import autograd.numpy as np
from autograd import grad


def oneline(x):
    y = x/2
    return y

grad_oneline = grad(oneline)
print(grad_oneline(3.0))

运行代码,传入任意X值,你就能得到在该X值下的斜率:

(base) G:\push\20220724>python 1.py
0.5

由于这是一条直线,因此无论你传什么值,都只会得到0.5的结果。

那么让我们再试试一个tanh函数:

# 公众号 Python实用宝典
import autograd.numpy as np
from autograd import grad

def tanh(x):
    y = np.exp(-2.0 * x)
    return (1.0 - y) / (1.0 + y)
grad_tanh = grad(tanh)
print(grad_tanh(1.0))

此时你会获得 1.0 这个 x 在tanh上的曲线的斜率:

(base) G:\push\20220724>python 1.py
0.419974341614026

我们还可以绘制出tanh的斜率的变化的曲线:

# 公众号 Python实用宝典
import autograd.numpy as np
from autograd import grad


def tanh(x):
    y = np.exp(-2.0 * x)
    return (1.0 - y) / (1.0 + y)
grad_tanh = grad(tanh)
print(grad_tanh(1.0))

import matplotlib.pyplot as plt
from autograd import elementwise_grad as egrad
x = np.linspace(-7, 7, 200)
plt.plot(x, tanh(x), x, egrad(tanh)(x))
plt.show()

图中蓝色的线是tanh,橙色的线是tanh的斜率,你可以非常清晰明了地看到tanh的斜率的变化。非常便于学习和理解斜率概念。

3.实现一个逻辑回归模型

有了Autograd,我们甚至不需要借用scikit-learn就能实现一个回归模型:

逻辑回归的底层分类就是基于一个sigmoid函数:

import autograd.numpy as np
from autograd import grad

# Build a toy dataset.
inputs = np.array([[0.52, 1.12,  0.77],
                   [0.88, -1.08, 0.15],
                   [0.52, 0.06, -1.30],
                   [0.74, -2.49, 1.39]])
targets = np.array([True, True, False, True])

def sigmoid(x):
    return 0.5 * (np.tanh(x / 2.) + 1)

def logistic_predictions(weights, inputs):
    # Outputs probability of a label being true according to logistic model.
    return sigmoid(np.dot(inputs, weights))

从下面的损失函数可以看到,预测结果的好坏取决于weights的好坏,因此我们的问题转化为怎么优化这个 weights 变量:

def training_loss(weights):
    # Training loss is the negative log-likelihood of the training labels.
    preds = logistic_predictions(weights, inputs)
    label_probabilities = preds * targets + (1 - preds) * (1 - targets)
    return -np.sum(np.log(label_probabilities))

知道了优化目标后,又有Autograd这个工具,我们的问题便迎刃而解了,我们只需要让weights往损失函数不断下降的方向移动即可:

# Define a function that returns gradients of training loss using Autograd.
training_gradient_fun = grad(training_loss)

# Optimize weights using gradient descent.
weights = np.array([0.0, 0.0, 0.0])
print("Initial loss:", training_loss(weights))
for i in range(100):
    weights -= training_gradient_fun(weights) * 0.01

print("Trained loss:", training_loss(weights))

运行结果如下:

(base) G:\push\20220724>python regress.py
Initial loss: 2.772588722239781
Trained loss: 1.067270675787016

由此可见损失函数以及下降方式的重要性,损失函数不正确,你可能无法优化模型。损失下降幅度太单一或者太快,你可能会错过损失的最低点。

总而言之,AutoGrad是一个你用来优化模型的一个好工具,它可以给你提供更加直观的损失走势,进而让你有更多优化想象力。有兴趣的朋友还可以看官方的更多示例代码:

我们的文章到此就结束啦,如果你喜欢今天的 Python 教程,请持续关注Python实用宝典。

有任何问题,可以在公众号后台回复:加群,回答相应验证信息,进入互助群询问。

原创不易,希望你能在下面点个赞和在看支持我继续创作,谢谢!

给作者打赏,选择打赏金额
¥1¥5¥10¥20¥50¥100¥200 自定义

​Python实用宝典 ( pythondict.com )
不只是一个宝典
欢迎关注公众号:Python实用宝典

如何使用Python预测机票价格

印度的机票价格基于供需关系浮动,很少受到监管机构的限制。因此它通常被认为是不可预测的,而动态定价机制更增添了人们的困惑。

我们的目的是建立一个机器学习模型,根据历史数据预测未来航班的价格,这些航班价格可以给客户或航空公司服务提供商作为参考价格。

1.准备

开始之前,你要确保Python和pip已经成功安装在电脑上,如果没有,请访问这篇文章:超详细Python安装指南 进行安装。

如果你用Python的目的是数据分析,可以直接安装Anaconda:Python数据分析与挖掘好帮手—Anaconda,它内置了Python和pip.

此外,推荐大家用VSCode编辑器来编写小型Python项目:Python 编程的最好搭档—VSCode 详细指南

Windows环境下打开Cmd(开始—运行—CMD),苹果系统环境下请打开Terminal(command+空格输入Terminal),输入命令安装依赖:

pip install pandas
pip install numpy
pip install matplotlib
pip install seaborn
pip install scikit-learn

2.导入相关数据集

本文的数据集是 Data_Train.xlsx,首先看看训练集的格式:

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
sns.set_style('whitegrid')


flights = pd.read_excel('./Data_Train.xlsx')
flights.head()

可见训练集中的字段有航空公司(Airline)、日期(Date_of_Journey)、始发站(Source)、终点站(Destination)、路线(Route)、起飞时间(Dep_Time)、抵达时间(Arrival_Time)、历经时长(Duration)、总计停留站点个数(Total_Stops)、额外信息(Additional_Info),最后是机票价格(Price)。

与其相对的测试集,除了缺少价格字段之外,与训练集的其他所有字段均一致。

访问:https://pythondict.com/download/predict-ticket/

或在Python实用宝典后台回复:预测机票,下载完整数据源和代码。

3.探索性数据分析

3.1 清理缺失数据

看看所有字段的基本信息:

flights.info()

其他的非零值数量均为10683,只有路线和停靠站点数是10682,说明这两个字段缺少了一个值。

谨慎起见,我们删掉缺少数据的行:

# clearing the missing data
flights.dropna(inplace=True)
flights.info()

现在非零值达到一致数量,数据清理完毕。

3.2 航班公司分布特征

接下来看看航空公司的分布特征:

sns.countplot('Airline', data=flights)
plt.xticks(rotation=90)
plt.show()

前三名的航空公司分别是 IndiGo, Air India, JetAirways.

其中可能存在廉价航空公司。

3.3 再来看看始发地的分布

sns.countplot('Source',data=flights)
plt.xticks(rotation=90)
plt.show()

某些地区可能是冷门地区,存在冷门机票的可能性比较大。

3.4 停靠站点的数量分布

sns.countplot('Total_Stops',data=flights)
plt.xticks(rotation=90)
plt.show()

看来大部分航班在飞行途中只停靠一次或无停靠。

会不会某些停靠多的航班比较便宜?

3.5 有多少数据含有额外信息

plot=plt.figure()
sns.countplot('Additional_Info',data=flights)
plt.xticks(rotation=90)

大部分航班信息中都没有包含额外信息,除了部分航班信息有:不包含飞机餐、不包含免费托运。

这个信息挺重要的,是否不包含这两项服务的飞机机票比较便宜?

3.6 时间维度分析

首先转换时间格式:

flights['Date_of_Journey'] = pd.to_datetime(flights['Date_of_Journey'])
flights['Dep_Time'] = pd.to_datetime(flights['Dep_Time'],format='%H:%M:%S').dt.time

接下来,研究一下出发时间和价格的关系:

flights['weekday'] = flights[['Date_of_Journey']].apply(lambda x:x.dt.day_name())
sns.barplot('weekday','Price',data=flights)
plt.show()

大体上价格没有差别,说明这个特征是无效的。

那么月份和机票价格的关系呢?

flights["month"] = flights['Date_of_Journey'].map(lambda x: x.month_name())
sns.barplot('month','Price',data=flights)
plt.show()

没想到4月的机票价格均价只是其他月份的一半,看来4月份是印度的出行淡季吧。

起飞时间和价格的关系

flights['Dep_Time'] = flights['Dep_Time'].apply(lambda x:x.hour)
flights['Dep_Time'] = pd.to_numeric(flights['Dep_Time'])
sns.barplot('Dep_Time','Price',data=flights)
plot.show()

可以看到,红眼航班(半夜及早上)的机票比较便宜,这是符合我们的认知的。

3.7 清除无效特征

把那些和价格没有关联关系的字段直接去除掉:

flights.drop(['Route','Arrival_Time','Date_of_Journey'],axis=1,inplace=True)
flights.head()

4.模型训练

接下来,我们可以准备使用模型来预测机票价格了,不过,还需要对数据进行预处理和特征缩放。

4.1 数据预处理

将字符串变量使用数字替代:

from sklearn.preprocessing import LabelEncoder
var_mod = ['Airline','Source','Destination','Additional_Info','Total_Stops','weekday','month','Dep_Time']
le = LabelEncoder()
for i in var_mod:
    flights[i] = le.fit_transform(flights[i])
flights.head()

对每列数据进行特征缩放,提取自变量(x)和因变量(y):

flights.corr()
def outlier(df):
    for i in df.describe().columns:
        Q1=df.describe().at['25%',i]
        Q3=df.describe().at['75%',i]
        IQR= Q3-Q1
        LE=Q1-1.5*IQR
        UE=Q3+1.5*IQR
        df[i]=df[i].mask(df[i]<LE,LE)
        df[i]=df[i].mask(df[i]>UE,UE)
    return df
flights = outlier(flights)
x = flights.drop('Price',axis=1)
y = flights['Price']

划分测试集和训练集:

from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=101)

4.2 模型训练及测试

使用随机森林进行模型训练:

from sklearn.ensemble import RandomForestRegressor
rfr=RandomForestRegressor(n_estimators=100)
rfr.fit(x_train,y_train)

在随机森林中,我们有一种根据数据的相关性来确定特征重要性的方法:

features=x.columns
importances = rfr.feature_importances_
indices = np.argsort(importances)
plt.figure(1)
plt.title('Feature Importances')
plt.barh(range(len(indices)), importances[indices], color='b', align='center')
plt.yticks(range(len(indices)), features[indices])
plt.xlabel('Relative Importance')

可以看到,Duration(飞行时长)是影响最大的因子。

对划分的测试集进行预测,得到结果:

predictions=rfr.predict(x_test)
plt.scatter(y_test,predictions)
plt.show()

这样看不是很直观,接下来我们要数字化地评价这个模型。

4.3 模型评价

sklearn 提供了非常方便的函数来评价模型,那就是 metrics :

from sklearn import metrics
print('MAE:', metrics.mean_absolute_error(y_test, predictions))
print('MSE:', metrics.mean_squared_error(y_test, predictions))
print('RMSE:', np.sqrt(metrics.mean_squared_error(y_test, predictions)))
print('r2_score:', (metrics.r2_score(y_test, predictions)))
MAE: 1453.9350628905618
MSE: 4506308.3645551
RMSE: 2122.806718605135
r2_score: 0.7532074710409375

这4个值中你可以只关注R2_score,r2越接近1说明模型效果越好,这个模型的分数是0.75,算是很不错的模型了。

看看其残差直方图是否符合正态分布:

sns.distplot((y_test-predictions),bins=50)
plt.show()

不错,多数预测结果和真实值都在-1000到1000的范围内,算是可以接受的结果。其残差直方图也基本符合正态分布,说明模型是有效果的。

部分译自 https://www.kaggle.com/harikrishna9/how-to-predict-flight-ticket-price/notebook,有较多的增删。

我们的文章到此就结束啦,如果你喜欢今天的 Python 教程,请持续关注Python实用宝典。

有任何问题,可以在公众号后台回复:加群,回答相应验证信息,进入互助群询问。

原创不易,希望你能在下面点个赞和在看支持我继续创作,谢谢!

给作者打赏,选择打赏金额
¥1¥5¥10¥20¥50¥100¥200 自定义

​Python实用宝典 ( pythondict.com )
不只是一个宝典
欢迎关注公众号:Python实用宝典

Python 短文本自动识别个体是否有自杀倾向

上一篇文章,我们以微博树洞为例,讲解了怎么自动爬取单个微博的评论。今天我们就要用上这些数据做一个自杀倾向分类器,这样的分类器如果应用得当,将可以帮助成千上万误入歧途的人们挽回生命。

为了简化问题,我们将短文本分为两种类别中的一种,即要么是正常微博、要么是自杀倾向微博。这样,有了上次的微博树洞,训练集和测试集就非常好获得了。由于是短文本二分类问题,可以使用scikit-learn的SVM分类模型。

不过要注意的是,我们的分类器并不能保证分类出来的结果百分百正确,毕竟心理状态是很难通过文本准确识别出来的,我们只能通过文字,大致判断其抑郁情况并加以介入。实际上这是一个宁可错杀一百,不可放过一个的问题。毕竟放过一个,可能就有一条生命悄然流逝。

本文源代码: https://github.com/Ckend/suicide-detect-svm 欢迎一同改进这个项目,在训练集和模型方面,改进的空间还相当大。如果你访问不了github,请关注文章最下方公众号,回复自杀倾向检测获得本项目完整源代码。

2023-04-26更新:

提供一个5W行的数据源,数据结构请自行组合:https://pythondict.com/download/%e8%b5%b0%e9%a5%ad%e5%be%ae%e5%8d%9a%e8%af%84%e8%ae%ba%e6%95%b0%e6%8d%ae/

1.数据准备

数据集整体上分两个部分,一部分是训练集、一部分是测试集。其中,训练集和测试集中还要分为正常微博短文本和自杀倾向短文本。

将上一篇爬取微博树洞的文章中得到的数据进行人工筛选后,挑出300条作为训练集(有点少,其实业界至少也要3000条以上),再根据上次的微博爬虫随意爬取10000条微博作为训练集的正常微博类。另外再分别搜集自杀倾向微博和普通微博各50条作为测试集。

每条微博按行存储在txt文件里。训练集中,正常微博命名为normal.txt, 自杀倾向微博命名为die.txt。测试集存放在后缀为_test.txt的文件中:

此外,接下来我们会使用到一个机器学习工具包叫scikit-learn(sklearn),其打包好了许多机器学习模型和预处理的方法,方便我们构建分类器,在CMD/Terminal输入以下命令安装:

pip install -U scikit-learn

如果你还没有安装Python,请看这篇文章安装Python,然后再执行上述命令安装sklearn.

2.数据预处理

我们使用一个典型的中文自然语言预处理方法:对文本使用结巴分词后将其数字化。

由于具有自杀倾向的微博中,其实类似于”死”、”不想活”、”我走了”等这样的词语比较常见,因此我们可以用TF-IDF将字符串数字化。如果你不了解TF-IDF,请看这篇文章: 文本处理之 tf-idf 算法及其实践

数字化的部分代码如下。

print('(2) doc to var...')
from sklearn.feature_extraction.text import CountVectorizer, TfidfTransformer

# CountVectorizer考虑每种词汇在该训练文本中出现的频率,得到计数矩阵
count_v0= CountVectorizer(analyzer='word',token_pattern='\w{1,}')
counts_all = count_v0.fit_transform(all_text)

count_v1= CountVectorizer(vocabulary=count_v0.vocabulary_)
counts_train = count_v1.fit_transform(train_texts) 
print("the shape of train is "+repr(counts_train.shape)  )
count_v2 = CountVectorizer(vocabulary=count_v0.vocabulary_)
counts_test = count_v2.fit_transform(test_texts)
print("the shape of test is "+repr(counts_test.shape)  )

# 保存数字化后的词典
joblib.dump(count_v0.vocabulary_, "model/die_svm_20191110_vocab.m")

counts_all = count_v2.fit_transform(all_text)
print("the shape of all is "+repr(counts_all.shape))

# 将计数矩阵转换为规格化的tf-idf格式
tfidftransformer = TfidfTransformer()  
train_data = tfidftransformer.fit(counts_train).transform(counts_train)
test_data = tfidftransformer.fit(counts_test).transform(counts_test)
all_data = tfidftransformer.fit(counts_all).transform(counts_all) 

3.训练

使用scikit-learn的SVM分类模型,我们能很快滴训练并构建出一个分类器:

print('(3) SVM...')
from sklearn.svm import SVC

# 使用线性核函数的SVM分类器,并启用概率估计(分别显示分到两个类别的概率如:[0.12983359 0.87016641])
svclf = SVC(kernel = 'linear', probability=True) 

# 开始训练
svclf.fit(x_train,y_train)
# 保存模型
joblib.dump(svclf, "model/die_svm_20191110.m")

这里我们忽略了SVM原理的讲述,SVM的原理可以参考这篇文章:支持向量机(SVM)——原理篇

4.测试

测试的时候,我们要分别计算模型对两个类别的分类精确率和召回率。scikit-learn提供了一个非常好用的函数classification_report来计算它们:

# 测试集进行测试
preds = svclf.predict(x_test)
y_preds = svclf.predict_proba(x_test)

preds = preds.tolist()
for i,pred in enumerate(preds):
    # 显示被分错的微博
    if int(pred) != int(y_test[i]):
        try:
            print(origin_eval_text[i], ':', test_texts[i], pred, y_test[i], y_preds[i])
        except Exception as e:
            print(e)

# 分别查看两个类别的准确率、召回率和F1值
print(classification_report(y_test, preds)) 

结果:

对自杀倾向微博的分类精确率为100%,但是查全率不够,它只找到了50条里的60%,也就是30条自杀倾向微博。

对于正常微博的分类,其精确率为71%,也就是说有部分正常微博被分类为自杀倾向微博,不过其查全率为100%,也就是不存在不被分类的正常微博。

这是建立在训练集还不够多的情况下的结果。我们的自杀倾向微博的数据仅仅才300条,这是远远不够的,如果能增加到3000条,相信结果会改进不少,尤其是对于自杀倾向微博的查全率有很大的帮助。预估最终该模型的精确率和召回率至少能达到95%。

本文源代码: https://github.com/Ckend/suicide-detect-svm 欢迎一同改进这个项目。如果你访问不了github,请关注文章最下方公众号,回复自杀倾向检测获得本项目完整源代码。

如果你喜欢今天的Python 教程,请持续关注Python实用宝典,如果对你有帮助,麻烦在下面点一个赞/在看哦有任何问题都可以在下方留言区留言,我们会耐心解答的!

​Python实用宝典 (pythondict.com)
不只是一个宝典
欢迎关注公众号:Python实用宝典

为什么Python这么适合AI和机器学习?4位专家给你答案

Python是机器学习的最佳编程语言之一,其迅速地在学术界和相关研究领域威胁到甚至超过了R的主导地位。为什么Python在机器学习领域如此受欢迎呢?为什么Python对AI有好处?

Mike Driscoll采访了4位Python专家和机器学习社区人士,讨论了Python在AI和相关研究领域中如此受欢迎的原因。

编程是一种社会活动——Python的社区已经透彻认识到了这一点

Glyph Lefkowitz

Glyph Lefkowitz (@glyph)是Python网络编程框架Twisted(一个能用于Python异步开发的)的创始人,他在2017年获得了PSF的社区服务奖。他是这么认为的:

人工智能是一个包罗万象的术语,它倾向于指当前计算机科学研究中最先进的领域。

曾经有一段时间,我们理所当然的以为基本图形遍历被认为是AI。在那个时候,Lisp是一种大型人工智能语言,因为它比一般的编程语言要高级,而且研究人员可以更容易地用Lisp做快速原型。我认为Python在总体上已经很大程度上取代了它,因为除了具有类似的高级功能外,它还有一个优秀的第三方生态系统。

Lispers会反对,所以我应该说清楚,我并不是要捧高Python的地位,只是说Python和Lisp都是同一类语言,比如在垃圾回收、内存安全等机制、命名空间和高级数据结构等方面都具有相似的特点。

从机器学习的更具体的意义上说,也就是现在越来越多的人所说的人工智能,我认为有更具体的答案。NumPy的存在及其伴随的生态系统允许非常适合研究的高级内容的混合,以及非常高性能的数字处理。如果不是非常密集的数字运算,机器学习就什么都不是。

“……统计学家、天文学家、生物学家和业务分析师都已经成为Python程序员,并改进了这个语言。”

Python社区致力于为非程序员提供友好的介绍和生态系统支持,这确实增加了它在数据科学和科学计算的姊妹学科中的应用。无数的统计学家、天文学家、生物学家和业务分析师已经成为Python程序员,并改进了这个语言。编程本质上是一种社会活动,Python社区比除JavaScript之外的任何语言都更认可这一点。

Python让用户关注真正的问题

Marc-Andre Lemburg

Marc-Andre Lemburg (@malemburg),PSF的联合创始人和eGenix的首席执行官。他是这样认为的:

Python对于没有受过计算机科学训练的人来说非常容易理解。当您试图执行研究所需的外部时,它消除了您必须处理的许多复杂性。

在Numeric(现在是NumPy)开始开发之后,新增了IPython笔记本(现在是Jupyter笔记本)、matplotlib和许多其他工具,使事情变得更加直观,Python允许使用者主要考虑问题的解决方案,而不是驱动这些解决方案所需的技术。

“Python是一种理想的集成语言,可以轻松地将技术绑定在一起。”

Python是一种理想的集成语言,可以轻松地将技术绑定在一起。Python允许用户关注真正的问题,而不是将时间花在实现细节上。除了让用户更容易操作之外,对于开发外部的底层集成人员来说,Python还是一个理想的粘合平台。这主要是因为Python非常容易通过一个漂亮而完整的C语言API访问。

Python有许多适合科学计算的特性

Luciano Ramalho

Luciano Ramalho (@ramalhoorg), ThoughtWorks技术负责人,PSF研究员。他是这么看的:

最重要和最直接的原因是NumPy和SciPy支持像scikit-learn这样的项目,scikit-learn目前几乎是机器学习的标准工具

首先创建NumPy、SciPy、scikit-learn和许多其他的原因是Python具有一些特性,使其对科学计算非常方便。Python有一个简单且一致的语法,这使得非软件工程师更容易进行编程。

“Python得益于科学计算的丰富生态系统。”

另一个原因是运算符重载,这使得代码可读且简洁。然后是Python的buffer protocol (PEP 3118),它是外部在处理类似数组的数据结构时与Python有效互操作的标准。最后,Python得益于科学计算的丰富生态系统,这吸引了更多的科学家与开发者,并创造了一个良性循环。

Python严格一致的特点使其对AI非常友好

Mike Bayer

Mike Bayer (@zzzeek), Red Hat高级软件工程师,SQLAlchemy的创建者。他是这么认为的:

我们在这个领域所做的是开发我们的数学模型和算法。我们正在把我们肯定想要保留的和优化的算法放入像scikit-learn这样的中。然后,我们将继续迭代并共享关于如何组织和考虑数据的注释。

高级脚本语言是人工智能和机器学习的理想语言,因为我们可以快速转移数据,然后再试一次。我们创建的代码的大部分用于表示实际的数学和数据结构。

像Python这样的脚本语言甚至更好,因为它是严格和一致的。每个人都可以更好地理解彼此的Python代码,而不是使用其他具有混乱和不一致编程范例的语言。

Python重视我们正在努力做的工作的核心,并且完全最小化了我们如何给计算机下达指令等其他事情,这是应该的,自动化所有你不应该考虑的事情,让你更好地为了达到目标写代码。

我们的文章到此就结束啦,如果你希望我们今天的Python 教程,请持续关注我们,如果对你有帮助,麻烦在下面点一个赞/在看哦有任何问题都可以在下方留言区留言,我们都会耐心解答的!


​Python实用宝典 (pythondict.com)
不只是一个宝典
欢迎关注公众号:Python实用宝典

准确率94%!Python 机器学习识别微博或推特机器人

推特或微博机器人的存在其实是比较危险的,他们可以制造虚假的流量、传播谣言、甚至执行一些令人汗颜的恶意操作,这里我们使用kaggle纽约大学2017年机器学习竞赛的推特分类数据来进行我们的识别实验,本实验的数据集请访问:下载Python推特机器人分类数据集

在开始之前我们需要安装以下Python包(),打开你的CMD(Windows系统)/Terminal(macOS系统)输入以下指令即可:

pip install numpy
pip install seaborn
pip install pandas
pip install matplotlib
pip install scikit-learn

其中numpy和pandas都是用于数据处理的,numpy是一个C编写的,所以运算会比python内置的运算快,此外,matplotlib和seaborn主要用于Python数据可视化。scikit-learn内置了许多常用的机器学习分析模型,用起来非常简单。

1.Python加载数据

好了,废话不多说,让我们现在就开始使用panda加载数据,分别获得bot和非bot数据:

import pandas as pd
import numpy as np
import seaborn
import matplotlib

data = pd.read_csv('training_data.csv')
Bots = data[data.bot==1]
NonBots = data[data.bot==0]

使用热力图识别训练集/测试集中缺失数据:

seaborn.heatmap(data.isnull(), yticklabels=False, cbar=False, cmap='viridis')
# 热力图,当data中有空值时标为黄色
matplotlib.pyplot.tight_layout() 
matplotlib.pyplot.show()
Python识别缺失数据

2.Python 特征选择

什么是特征选择?其实很简单,我们在日常生活中识别西瓜和榴莲的时候是怎么识别的?比如从外观特征上:榴莲带刺、黄色的;西瓜圆润、绿色的。机器学习模型也是一样的,我们需要从类似于挑选西瓜外观特征来挑选两个类别的特征。比如说应用上我们前面的Python热力图查看数据缺失

Python机器人的数据缺失热力图
Python非机器人的数据缺失热力图

我们可以明显地看到机器人的location, urls明显缺失的部分更多。因此我们的特征可以加上这两项,由于数据量不多,我们应该绕过字符串编码,以location列为例,编码方式为:如果location缺失则为false, location存在则为True.

其他特征当然还有比如姓名、描述(description ) 这样的必输信息。当然,我们还能通过选择Twitter机器人使用的一些不好的单词将他们作为特征,如果他们的信息里包含了这些脏话,则将该机器人的该项特征设为True。下面是一个机器人使用脏话的例子。你可以添加更多的单词:

bag_of_words_bot = r'bot|b0t|cannabis|tweet me|mishear|follow me|updates every|gorilla|yes_ofc|forget' \
r'expos|kill|bbb|truthe|fake|anony|free|virus|funky|RNA|jargon'\
r'nerd|swag|jack|chick|prison|paper|pokem|xx|freak|ffd|dunia|clone|genie|bbb' \
r'ffd|onlyman|emoji|joke|troll|droop|free|every|wow|cheese|yeah|bio|magic|wizard|face'

将我们的特征编码为数字的形式:

# 该列的每个值包不包含脏话,包含则为True,不包含则为False
data['screen_name_binary'] = data.screen_name.str.contains(bag_of_words_bot, case=False, na=False)
data['name_binary'] = data.name.str.contains(bag_of_words_bot, case=False, na=False)
data['description_binary'] = data.description.str.contains(bag_of_words_bot, case=False, na=False)
data['status_binary'] = data.status.str.contains(bag_of_words_bot, case=False, na=False)

# 判断该列的每个值是否有listedcount>20000的情况,有的话为False,没有的话为True
data['listed_count_binary'] = (data.listedcount>20000)==False 

# 判断该列的每个值是否有空的情况,有空的则为False,否则为True
data['location_binary'] = ~data.location.isnull()
data['url_binary'] = ~data.url.isnull()

# 选定我们的特征
features = ['screen_name_binary', 'name_binary', 'description_binary', 'status_binary', 'verified', 'followers_count','verified', 'friends_count', 'statuses_count', 'listed_count_binary', 'bot', 'url_binary', 'location_binary', 'default_profile', 'default_profile_image'] 

其中需要注意的是所有的文本我们都编码为0和1的形式(存不存在脏话)。

3.Python scikit-learn训练与测试

现在让我们来使用Python scikit-learn包里的决策树模型进行分类。

首先我们引入需要使用到的包,有三个,如下:

from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split 

1.从 sklearn.tree 引入 DecisionTreeClassifier ,这是一个决策树的分类器模型,我们一会将使用它进行训练;
2.sklearn.metrics 引入 accuracy_score 这是用于方便计算准确率的;
3.sklearn.model_selection 的 train_test_split 是用于方便分割训练集和测试集的。

分割训练集

X = data[features].iloc[:,:-1] 
# 除了最后一列的BOT都是数据
y = data[features].iloc[:,-1] 
# BOT是分类对象,1:机器人 0:非机器人X_train, 
X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=101)

使用到了train_test_split()函数,test_size=0.3即30%的数据用于测试,random_state=101 是随机数种子,设置后对于每次不改变训练集的测试,测试结果都一样。

训练与测试

clf = DecisionTreeClassifier(criterion='entropy', min_samples_leaf=50, min_samples_split=10)
clf.fit(X_train, y_train)
y_pred_train = clf.predict(X_train)
y_pred_test = clf.predict(X_test)  
print("Training Accuracy: %.5f" %accuracy_score(y_train, y_pred_train))
print("Test Accuracy: %.5f" %accuracy_score(y_test, y_pred_test)) 

初始化了一个决策树模型clf,clf.fit即开始进行训练,clf.predict则为测试。

4.Python 模型结果

测试结果

最终我得到的结果如下,测试准确率高达94.4%,这是一个相当令人满意的结果了,在当时的kaggle比赛里大约能排在27名左右。你也可以尝试其他的模型,并非只有决策树可以选择,比如说SVM、LR都可以尝试一下。

kaggle排名

全部源代码下载请点击:Python机器学习识别微博或推特机器人(acc:94.4%)

我们的文章到此就结束啦,如果你希望我们今天的Python 教程,请持续关注我们,如果对你有帮助,麻烦在下面点一个赞/在看哦有任何问题都可以在下方留言区留言,我们都会耐心解答的!


​Python实用宝典 (pythondict.com)
不只是一个宝典
欢迎关注公众号:Python实用宝典

[准确率:98%] Python 改进朴素贝叶斯自动分类食品安全新闻 实战教程

模型成品在极致安食网:https://jizhianshi.com, 木有太多时间维护。

这是本系列第二篇文章,位于源代码的 2. NB_Weights 中:

https://github.com/Ckend/NLP_DeepLearning_CN_Tutorial

前一篇文章中,我们学习了如何使用朴素贝叶斯自动分类食品安全新闻,准确率为97%,这一篇文章将教大家如何改进这个模型。阅读本篇文章之前,建议先阅读前一篇文章:[准确率:97%] 朴素贝叶斯自动分类食品安全新闻,否则有些概念可能无法理解。

在那篇文章中,在训练的时候,朴素贝叶斯模型中所有词语都是相同的权重,而事实上真的如此吗?我们怎么样才可以知道哪些词语更加重要呢?这时候,数理统计就派上用场了。

我们先对所有的食品安全新闻和非食品安全新闻使用结巴(jieba)分词, 然后统计各个词性在这分别在这两个类别中的数量,比如说名词的结果如下表(使用SPSS得到,其他词性就不一一展示了),显然食品安全新闻中名词的数量多于非食品安全新闻,这也是在人意料之中的结果,但是这并不代表着对于食品安全新闻,名词的重要性就大于其他的词性:

那么如何确定各个词性对分类的重要性呢?单纯根据频率和频数确定是比较复杂的,我们可以尝试使用我们的模型,比如说,先得到一个基准的准确值,然后尝试去除掉名词得到一个准确值,观察这两个准确值的差距,如果非常大,说明名词具有比较重要的地位。我们可以试一下:

在所有词性权重都为1的情况下(基准)进行训练,准确率为:

如果说,名词权重为0呢?

不过,没有对比是没有意义的,我们尝试令形容词权重为0,看看怎么样:

可以看到几乎没有影响,也就是说,对于我们的分类器而言,名词的重要性远远大于形容词。这样,我们可以尝试增加名词的权重,看看效果怎么样:

准确率 0.98,效果显著,不过我们很难确定最佳的权重,只能通过(玄学)调参来找到最合适的权重,你也可以尝试用神经网络来确定权重,虽然容易过拟合,但也是一种方法。不过既然用上了神经网络,就有更优秀的模型可以使用了,我们下次再介绍吧。

接下来讲一下这个权重的实现原理(要是不耐烦,可以直接看文章首行的源代码):基于前一篇文章,我们修改jieba_cut_and_save_file这个函数,这个函数在训练的时候用到了,它修改的就是训练时每个新闻被向量化而成的值,如下所示:

将该函数改成这样:

def jieba_cut_and_save_file(inputList, n_weight, a_weight, output_cleaned_file=False):

   """
   1. 读取中文文件并分词句子
   2. 可以将分词后的结果保存到文件
   """

    output_file = os.path.join('./data/', 'cleaned_' + 'trainMatrix.txt')
    lines = []
    tags = []

    for line in inputList:
        result = pseg.cut(clean_str(line))
        a = []
        b = []
        for word, flag in result:
            # 对分词后的新闻
            if word != ' ':
                # 若非空
                a.append(word)
                if flag.find('n')==0:
                    # 若是名词
                    b.append(n_weight)

                elif flag.find('a')==0:
                    # 若形容词
                    b.append(a_weight)
                else:
                    b.append(1)

        lines.append(a)
        tags.append(b)

    if output_cleaned_file:
        with open(output_file, 'w') as f:
            for line in lines:
                f.write(" ".join(line) + '\n')

    vocabulary = createVocabList(lines)

    # 根据词典生成词向量化器,并进行词向量化
    setOfWords2Vec = setOfWords2VecFactory(vocabulary)
    vectorized = []
    for i,news in enumerate(lines):
        vector = setOfWords2Vec(news, tags[i])
        vectorized.append(vector)

    return vectorized, vocabulary

最后,在主函数调用的时候传入你想需要的参数即可,你可以按照代码,修改任意词性权重,不过要注意结巴的词性表(每种词性的字母),你可以搜索“ictclas 词性表”得到。

import bayes
from data_helpers import *
from sklearn.externals import joblib


posFile = "./data/train_food.txt"
negFile = "./data/train_notfood.txt"



print("正在获取训练矩阵及其分类向量")
trainList,classVec = loadTrainDataset(posFile,negFile)

print("正在将训练矩阵分词,并生成词表")
n_weight = 3
# 名词权重
a_weight = 1
# 形容词权重

vectorized, vocabulary = jieba_cut_and_save_file(trainList, n_weight, a_weight, True)
bayes_ = bayes.oldNB(vocabulary)

# 初始化模型
print("正在训练模型")
bayes_.train(vectorized, classVec)

# 训练
print("保存模型")

joblib.dump(bayes_, "./arguments/train_model.m")

最后总结一下,我们通过修改训练集新闻中不同词性的权重,加大名词权重,从而提高朴素贝叶斯模型的准确率到98%,不过,这个仅仅是在食品安全新闻这个例子中是这样,你如果想要应用到自己的程序上,应该先找到你的关键词性。下一篇文章我们将尝试一些新的模型元素。如果你喜欢的话,请继续关注幻象客。

[准确率:97%] Python 朴素贝叶斯自动分类食品安全新闻

模型成品在极致安食网:https://jizhianshi.com, 木有太多时间维护。

教程源代码点击阅读原文或访问:

https://github.com/Ckend/NLP_DeepLearning_CN_Tutorial

利用朴素贝叶斯来分类食品安全新闻(标题)这种短文本其实精确度并不高,在实际的生产中,由于食品安全和非食品安全的数量差异,我们会发现1000条新闻中可能才出现2条食品安全新闻,也就是说,即便你的模型准确率为95%,1000条新闻中依然会有许多新闻 (50条) 会被错分类,这是一个非常糟糕的结果。因此在生产环境中,如果模型准确率不能高达99%,甚至都无法使用。在我们的研究下,使用朴素贝叶斯,我们的准确率能提高到97%,使用改进的朴素贝叶斯准确率能达到98%以上,使用字符级的卷积神经网络甚至能达到99%,后续的教程我会介绍这两种方法。

为了后续的教程,我们还是先利用最简单的朴素贝叶斯来理解“训练”这个概念。为了尽量简化教程的难度,我尽量不使用数学公式进行讲解,更多的以自然语言和Python代码进行分析。在这个教程中,你所需要的东西有:

1. python 3

2. jieba 分词

3. numpy

4. sklearn (用joiblib保存模型)

训练

朴素贝叶斯的训练,其实就是遍历整个训练集,算出每个词语在不同的分类下出现的概率(该词语/该分类总词数)。最后得到两个向量,这两个向量分别代表了每个词语在食品安全新闻和非食品安全新闻中出现的概率。

for i in range(numTrainNews):

    if classVector[i] == 1:
        p1Num += trainMatrix[i]
        # 每条食品安全新闻中,每个词的数量分布

        p1Sum += sum(trainMatrix[i])
        # 求每条新闻出现的食品安全集合中的词语的数量总合

    else:
        p0Num += trainMatrix[i]
        p0Sum += sum(trainMatrix[i])

p1Vect = log(p1Num / p1Sum)
# 在1的情况下每个词语出现的概率

p0Vect = log(p0Num / p0Sum)
# 在0的情况下每个词语出现的概率

#保存结果
self.p0V = p0Vect
self.p1V = p1Vect

如代码所示,numTrainNews是所有新闻的个数,trainMartix 是新闻进行分词,并数字化后的矩阵(先不用在意这里的数据结构,后面会介绍),其中的[i]代表是第几条新闻,classVector与trainMatrix相对应,它是这些新闻的正确分类(1或0,本例中,1代表食品安全新闻,0代表非食品安全新闻)。

当该新闻是食品安全新闻的时候,即classVector[1] == 1,把它所有词语的频数加到p1Num中, p1Num也是一个向量,保存了所有出现词语的频数。p1Sum是所有词语出现的次数总和。同理,当该新闻不是食品安全新闻的时候,也是使用同样的方式处理。

训练结束后,会得到两个总的概率向量,即p1V和p0V,这两个向量分别代表了每个词语在食品安全新闻和非食品安全新闻中出现的概率。

这里需要注意的是,我们最后对p1V和p0V取对数( np.log( ) )是为了在后续的分类中不会出现许多很小的数(试想一个词在训练集中只出现过1次,但是总词数却有几千个)相乘,这些很小的数相乘可能会导致最后结果被四舍五入为0。取对数能让y值从[0,1]被映射到[-∞, 0],从而避免出现相乘结果被四舍五入的情况。

分类

def classify_vector(self, vec2Classify):
    """
    分类函数,对输入词向量分类
    :param vec2Classify: 欲分类的词向量
    """

    vec2Classify = vec2Classify[0]
    p1 = sum(vec2Classify * self.p1V) + log(self.p1)
    # 元素相乘
    p0 = sum(vec2Classify * self.p0V) + log(1.0 - self.p1)

    if p1 - p0 > 0:
        return 1
    else:
        return 0

vec2Classify 是需要分类的新闻进行分词操作后每个词语在总词表中出现的次数,没有出现的则为0,有出现的为1。以”江西公布食品抽检情况 样品总体合格率95.67%”为例,它分词后的结果是:[‘江西’, ‘公布’, ‘食品’, ‘抽检’, ‘情况’, ‘ ‘, ‘样品’, ‘总体’, ‘合格率’, ‘95.67%’],转换成向量则如下图所示:

其中,像“你们”、“水果”、“购物”这样的词语是不在这条新闻里的,但是它在训练集的其他新闻里,因此总表中会出现它,但是在该新闻的向量中,它会被标记为0. 代表没有出现。

这里还需要注意的是p1这个变量,这个变量叫作“先验概率”,在我们的例子中就是:“现实生活中,多少条新闻里出现一条食品安全新闻”,根据统计,这个数值大概是1/125, 但是我偏向于保守一点的值,因此将其固定设置为1/200. 请注意,我们这里是手动设置先验概率,在scikit-learn等机器学习框架中,他们会根据训练集自动得到这个值,最典型的就是 MultinomialNB 和 GaussianNB,在我们的例子中实际上提升不了多少准确率,因此在这里不讨论。

将当前的词向量和我们刚获得的词向量相乘后,对每个词语得到的值求和,再乘以先验概率就是我们在每个分类上的结果,若该新闻在食品安全的结果大于非食品安全,则被分类为食品安全新闻。

将上面的训练和分类转换成数学公式来进行讲解,那就是这样的:

A:我们需要测试的词语  

B:来自食品安全相关新闻/不是来自食品安全相关新闻的概率。

判断一个单词分别归属两类的概率:

判断一篇新闻是否是食品安全相关的概率:

难怪数学家喜欢写公式,确实比文字简洁很多。


在下面开始讲预处理和模型之前,我需要先把我们的trainNB.py放在这里,好让大家在阅读的过程中回来翻阅,理解预处理和模型中得到的变量是用来干嘛的,以便更好地明白这些代码:

import bayes

from data_helpers import *

from sklearn.externals import joblib

posFile = "./data/train_food.txt"
negFile = "./data/train_notfood.txt"

print("正在获取训练矩阵及其分类向量")
trainList,classVec = loadTrainDataset(posFile,negFile)

print("正在将训练矩阵分词,并生成词表")
vectorized, vocabulary = jieba_cut_and_save_file(trainList,True)

bayes = bayes.oldNB(vocabulary)

# 初始化模型
print("正在训练模型")
bayes.train(vectorized,classVec)

# 训练
print("保存模型")
joblib.dump(bayes, "./arguments/train_model.m")

预处理

讲完训练和测试,你基本上已经知道朴素贝叶斯的运作方式了,接下来我们讲一下如何进行预处理,并从文本中构建词向量和总词表。以下函数都位于GitHub库(见文章首行)的data_helpers.py文件中。

1. 读取训练集

首先从文件中读入我们的训练数据,在我上传的GitHub库(见文章首行)中,我们的训练数据被存在1.NaiveBayes/data/中。

def loadTrainDataset(posFile,negFile):

    """

    便利函数,加载训练数据集

    :param pos: 多少条食品安全相关新闻

    :param neg: 多少条非食品安全相关新闻

    """

    trainingList = []  # 训练集
    classVec = []  # 分类向量
    # 录入食品安全相关的训练集
    posList = list(open(posFile, 'r').readlines())
    posVec = [1] * len(posList)
    trainingList += posList
    classVec += posVec

    # 录入非食品安全相关的训练集
    negList = list(open(negFile, 'r').readlines())
    negVec = [0] * len(negList)
    trainingList += negList
    classVec += negVec

    return trainingList, classVec

2.对新闻进行分词

我们需要对这些新闻进行分词,为了节省以后训练使用的时间,我们还要把分词的结果保存,同样地,这些结果会被保存到data中,名字为cleaned_trainMatrix.txt. 如下所示:

def jieba_cut_and_save_file(inputList, output_cleaned_file=False):

    """

    1. 读取中文文件并分词句子

    2. 可以将分词后的结果保存到文件

    3. 如果已经存在经过分词的数据文件则直接加载

    """

    output_file = os.path.join('./data/', 'cleaned_' + 'trainMatrix.txt')

    if os.path.exists(output_file):
        lines = list(open(output_file, 'r').readlines())
        lines = [line.strip('\n').split(' ') for line in lines]

    else:
        lines = [list(jieba.cut(clean_str(line))) for line in inputList]
        # 将句子进行clean_str处理后进行结巴分词
        lines = [[word for word in line if word != ' '] for line in lines]

    if output_cleaned_file:
        with open(output_file, 'w') as f:
            for line in lines:
                f.write(" ".join(line) + '\n')

    vocabulary = createVocabList(lines)
    # 根据词典生成词向量化器,并进行词向量化
    setOfWords2Vec = setOfWords2VecFactory(vocabulary)
    vectorized = [setOfWords2Vec(news) for news in lines]

    return vectorized, vocabulary

def clean_str(string):
    """
    1. 将除汉字外的字符转为一个空格
    2. 除去句子前后的空格字符
    """

    string = re.sub(r'[^\u4e00-\u9fff]', ' ', string)
    string = re.sub(r'\s{2,}', ' ', string)
    return string.strip()

下面介绍其中出现的createVocabList等函数。

3. 生成词典

还记得我们前面讲到的总词表吗,它记录了所有单词,它包含了所有新闻中出现的,但是不重复的词语。

def createVocabList(news_list):
    """
    从分词后的新闻列表中构造词典
    """
    # 创造一个包含所有新闻中出现的不重复词的列表。
    vocabSet = set([])
    for news in news_list:
        vocabSet = vocabSet | set(news)
        # |取并

    return list(vocabSet)

4.将分词后的新闻向量化

def setOfWords2VecFactory(vocabList):
    """
    通过给定词典,构造该词典对应的setOfWords2Vec
    """

    #优化:通过事先构造词语到索引的哈希表,加快转化
    index_map = {}
    for i, word in enumerate(vocabList):
        index_map[word] = i

    def setOfWords2Vec(news):
        """
        以在构造时提供的词典为基准词典向量化一条新闻
        """

        result = [0]*len(vocabList)
        for word in news:
            #通过默认值查询同时起到获得索引与判断有无的作用
            index = index_map.get(word, None)

            if index:
                result[index] = 1

        return result

    return setOfWords2Vec

5.最后是测试需要用到的向量化新闻

原理和前边对训练集的向量化相似,但是由于是测试时对单个新闻的向量化,因此我们把它分开了。

def vectorize_newslist(news_list, vocabulary):
    """
    将新闻列表新闻向量化,变成词向量矩阵
    注:如果没有词典,默认值为从集合中创造
    """

    # 分词与过滤

    cut_news_list = [list(jieba.cut(clean_str(news))) for news in news_list]

    # 根据词典生成词向量化器,并进行词向量化
    setOfWords2Vec = setOfWords2VecFactory(vocabulary)
    vectorized = [setOfWords2Vec(news) for news in cut_news_list]

    return vectorized, vocabulary

贝叶斯模型(bayes.py)

为了方便保存模型,我们将模型视为一个类,类的初始化如下。

def __init__(self, vocabulary):
    self.p1V = None
    self.p0V = None
    self.p1 = None
    self.vocabulary = vocabulary

其中,vocabulary是在运行完jieba_cut_and_save_file后获得的。在模型初始化的时候我们需要传入vocabulary,见前面的trainNB.py.

1.训练函数

在前面讲解训练的时候我曾经用到这里的一部分代码,下面是全部代码,其实要讲的都已经讲过了,一些细分方面的东西都已经写在注释中了。

def train(self, trainMatrix, classVector):
    """
    训练函数
    :param trainMatrix: 训练词向量矩阵
    :param classVector: 分类向量
    """

    numTrainNews = len(trainMatrix)
    # 多少条新闻

    numWords = len(trainMatrix[0])
    # 训练集一共多少个词语

    # pFood = sum(classVector) / float(numTrainNews)
    # 新闻属于食品安全类的概率
    pFood = float(1)/float(200)

    p0Num = ones(numWords)
    p1Num = ones(numWords)
    p0Sum = 2.0
    p1Sum = 2.0
    # 以上初始化概率,避免有零的存在使后面乘积结果为0

    # self.words_weight

    for i in range(numTrainNews):
        if classVector[i] == 1:
            p1Num += trainMatrix[i]
            # 每条食品安全新闻中,每个词的数量分布
            p1Sum += sum(trainMatrix[i])
            # 求每条新闻出现的食品安全集合中的词语的数量总合

        else:
            p0Num += trainMatrix[i]
            p0Sum += sum(trainMatrix[i])

    p1Vect = log(p1Num / p1Sum)
    # 在1的情况下每个词语出现的概率

    p0Vect = log(p0Num / p0Sum)
    # 在0的情况下每个词语出现的概率

    #保存结果

    self.p0V = p0Vect
    self.p1V = p1Vect
    self.p1 = pFood


2. 分类新闻

测试新闻的时候我们需要用到以下两个函数,一个是用来将新闻词向量化,一个是用来将词向量进行最后的分类操作。

def classify_news(self, news):
    """
    分类函数,对输入新闻进行处理,然后分类
    :param vec2Classify: 欲分类的新闻
    """

    vectorized, vocabulary = vectorize_newslist([news],self.vocabulary)

    return self.classify_vector(vectorized)

def classify_vector(self, vec2Classify):
    """
    分类函数,对输入词向量分类
    :param vec2Classify: 欲分类的词向量
    """

    vec2Classify = vec2Classify[0]
    p1 = sum(vec2Classify * self.p1V) + log(self.p1)
    # 元素相乘

    p0 = sum(vec2Classify * self.p0V) + log(1.0 - self.p1)

    if p1 - p0 > 0:
        return 1
    else:
        return 0

这样我们便完成了训练时所需要的所有函数,接下来尝试训练,在该文件夹中运行python trainNB.py. 训练并保存模型:

成功!接下来我们来测试这个模型:

import bayes
from data_helpers import *
from sklearn.externals import joblib

posFile = "./data/eval_food.txt"
negFile = "./data/eval_notfood.txt"

print("正在得到测试矩阵及其分类向量")
trainList,classVec = loadTrainDataset(posFile,negFile)
nb = joblib.load("arguments/train_model.m")

# 读取模型
results = []
for i in trainList:
    result = nb.classify_news(i)
    results.append(result)

# 测试模型
acc = 0.0
correct = 0
for i in range(len(classVec)):
    if results[i] == classVec[i]:
        correct += 1

acc = correct/len(classVec)

print("正确率为:"+str(acc))

正确率为97.7%.

这只是第一篇教程,后续我们还有办法对食品安全新闻更加精确地进行分类,喜欢大家喜欢,有问题可以在评论区进行讨论。