机器学习(二)--sklearn之逻辑斯蒂回归和朴素贝叶斯


上回说到,sklearn中的k近邻算法解决多分类问题。k近邻的基本步骤是:收集数据、创建分类器、训练、预测、评估性能、调参(参数就是k的值,也就是邻居的个数)。

分类问题,我们说过,是监督学习的一大类问题,包括二分类和多分类。接下来的几篇博文都说的是分类问题,包括逻辑斯蒂回归、朴素贝叶斯、支持向量机、k-决策树以及随机森林。而且主要以手写数字识别为案例,数据集和测试集都是一样的。我们的机器学习重点在于用现有框架解决实际问题,这对于初学者是很有帮助的。当现有的库用得滚瓜烂熟时,再一步步深入研究这些算法的本质。

1、逻辑斯蒂回归

逻辑斯蒂回归(Logistic Regression),虽然带有“回归”二字,但它不是真正的回归算法,而是一个多分类算法。它是回归算法在离散数据方面的推广。另外一种类似的东西叫做支持向量机(SVM),但它只适用于解决二分类问题,解决多分类时需要特殊技巧来转化。

和k近邻一样,逻辑斯蒂回归在sklearn中也有现成的函数,只不过用法和k近邻大相径庭。逻辑斯蒂回归的原理比较复杂,涉及到非常艰深的概率论和线性代数知识,大致就是梯度下降算子什么的,这里就不细讲了。我们的目的,在于比较各种分类算法的效率和准确度。

逻辑斯蒂回归使用的基本步骤如下:

1、数据集收集和标准化(这个标准化是今天的全新内容)

2、创建逻辑斯蒂分类器(分类器有一个参数,叫做正则化度,用大写C表示,默认为1.0)

3、训练

4、检测训练集的训练效果

5、预测

6、检测预测准确度

首先数据集收集和上一节讲的完全相同。不会的回去看。

接着就是创建分类器。逻辑斯蒂分类器也是一个类对象,每调用一次创建函数就创建一个新的分类器。逻辑斯蒂回归分类器在sklearn.linear_model包中,记得import哦。代码和上一节的k近邻基本相同:

lrg = sklearn.linear_model.LogisticRegression(C=1.0)

其中C是刚才说的“正则化度”。C越大,则正则化度越弱(注意看清楚,这里是负相关的哦),实际使用的特征数量越多。我们稍后运行成功后,将手动调整它,看看不同的C对训练效果的影响。

训练和测试也是一样的。

lrg.fit(x_train_std, y_train)
lrg_result = lrg.predict(x_test_std)

然后是今天的重点之一:标准化。我们可以做一个实验,看看没有标准化的数据是什么效果。下面的代码:

import os
import sklearn.linear_model


def walk_files(directory, x, y):
    for root, dirs, files in os.walk(directory):
        for filename in files:
            vector = ""
            fp = open(directory + "\\" + filename, "r")
            line = fp.readline()
            while line:
                vector += line.strip("\n\r")
                line = fp.readline()
            fp.close()
            x.append(list(vector))
            y.append(int(filename[0]))


train_dir = "trainingDigits"
test_dir = "testDigits"
x_train = []
y_train = []
x_test = []
y_test = []

walk_files(train_dir, x_train, y_train)
walk_files(test_dir, x_test, y_test)
lrg = sklearn.linear_model.LogisticRegression() # 创建分类器
lrg.fit(x_train, y_train) # 训练
lrg_result = lrg.predict(x_test) # 预测

运行,提示

Traceback (most recent call last):
  File "D:/Program Files/JetBrains/PyCharm 2018.3.3/projects/py2/main.py", line 53, in <module>
    lrg_result = lrg.predict(x_test)
  File "D:\Program Files\Python36\lib\site-packages\sklearn\linear_model\base.py", line 281, in predict
    scores = self.decision_function(X)
  File "D:\Program Files\Python36\lib\site-packages\sklearn\linear_model\base.py", line 265, in decision_function
    dense_output=True) + self.intercept_
  File "D:\Program Files\Python36\lib\site-packages\sklearn\utils\extmath.py", line 173, in safe_sparse_dot
    return np.dot(a, b)
TypeError: Cannot cast array data from dtype('float64') to dtype('<U32') according to the rule 'safe'

我们看到,用原始数据对逻辑斯蒂分类器进行分类和预测是行不通的,会出现类型冲突(TypeError),这属于严重错误,甚至都通不过编译。所以,就引出了今天的标准化问题。

我们刚才传入fit函数和predict函数中的数据x_train和x_test,不能直接用于预测和训练。必须先用StandardScaler进行标准化。这个东西叫做标准化器,位于sklearn.preprocessing包中。它也是一个类对象,先创建后使用。创建方法如下:

ss = sklearn.preprocessing.StandardScaler() # ss就是一个标准化器

然后,用这个ss中的fit_transform函数对我们刚才收集到的x_train和x_test进行标准化。它们的返回值就是标准化后的数据。注意,只需要对数据(x_train)标准化,不需要对训练目标(y_train)标准化,因为训练目标(也就是类别)只能是整数或枚举,标准化是没有意义的。代码如下:

x_train_std = ss.fit_transform(x_train)
x_test_std = ss.fit_transform(x_test)

这样就得到标准化训练集x_train_std和标准化测试集x_test_std。接下来的事情就简单了,用这两个数据集进行训练和预测即可。代码:

lrg.fit(x_train_std, y_train)
lrg_result = lrg.predict(x_test_std)

这还不够,还需要评估训练效果和预测准确度。训练效果评估有现成的函数,也就是计算训练偏差系数R^2。在分类问题中,偏差系数就等于训练集上预测正确率。预测准确度当然是测试集上的预测正确率:

print("logistic R^2: %.3f" % (lrg.score(x_train_std, y_train)))
# lrg.score是专门计算训练效果的函数。是不是很方便啊
print("logistic score: %.3f" % (pandas.np.mean(lrg_result == y_test)))
# pd.np.mean(lrg_result == y_test)是计算两个列表对应位置相等的频率。也就是和我们上一节手写的那个评估是等价的。使用这个函数,需要import pandas

运行一下,结果为:

logistic R^2: 1.000
logistic score: 0.945

在训练集上,做到完美训练(即回归测试中没有错误数据)。测试集上没有k近邻表现好,昨天的k近邻准确度大约为98.7%。但是94.5%是可以接受的。

好了,现在我们来试试,改变正则化度C,对分类效果的影响。下面是一个表格:

正则化度C运行时间 /s训练集正确率测试集正确率
0.013.410.9860.936
0.024.480.9950.943
0.054.511.0000.944
0.15.251.0000.944
1.06.711.0000.945
2.06.711.0000.945
5.07.101.0000.943
10.07.111.0000.941
100.07.341.0000.939

注:运行时间指的是训练和预测的时间,也就是fit和predict的运行时间。

可见,随着正则化度提高,运行时间逐渐拉长,训练集正确率趋向于1。但测试集正确率是上凸形,在C=1.0~2.0之间达到峰值。这表明,正则化度低的时候,存在欠拟合,高的时候存在过拟合。最优的C是1.0左右。

2、朴素贝叶斯

朴素贝叶斯也是一种分类器。它基于概率论中的“贝叶斯公式”(也就是推广的条件概率公式)的算法。它的效率非常高,运行速度比k近邻和逻辑斯蒂等算法快2个数量级。在第三目中,我们将对比三种分类算法,来看看它们在效率和准确率方面的差异。

sklearn中,不出所料,朴素贝叶斯也是封装好的。使用朴素贝叶斯的步骤如下:

1、数据收集和归一化(归一化和逻辑斯蒂中的标准化差不多,是对数据进行预处理)

2、创建朴素贝叶斯分类器

3、训练

4、检测训练集的训练效果

5、预测

6、检测预测准确度

我们先说一下朴素贝叶斯分类器。朴素贝叶斯分类器又三种,高斯模型,多项式模型和伯努利模型。高斯模型假设样本概率呈正态分布,也就是连续数据。伯努利模型假设样本概率呈01分布。多项式模型则适用于多分类问题。对这三种模型感兴趣的童鞋自行百度。由于我们这里属于多分类问题,所以选择多项式模型。

然后,归一化的操作非常简单,和逻辑斯蒂中的标准化一样,调用一个MinMaxScaler函数,位于sklearn.preprocessing包中。

ss = sklearn.preprocessing.MinMaxScaler()
x_train_std = ss.fit_transform(x_train)
x_test_std = ss.fit_transform(x_test)

接下来就是创建、训练和测试。照猫画虎就行了。创建分类器时有一个参数alpha,是用于控制模型复杂度的,即所谓“平滑化”程度。它对模型性能影响不大,所以我们不用管它,按照默认的1.0到2.0之间即可。注意import sklearn.naive_bayes

nb = sklearn.naive_bayes.MultinomialNB(alpha=1.0)
nb.fit(x_train_std, y_train)
nb_result = nb.predict(x_test_std)
nb_relevant = nb.predict(x_train_std)
print("naive_bayes relevant: %.3f" % (pandas.np.mean(nb_relevant == y_train)))
print("naive_bayes score: %.3f" % (pandas.np.mean(nb_result == y_test)))

运行结果如下:

Bayes time: 0.013847
naive_bayes relevant: 0.928
naive_bayes score: 0.925

贝叶斯的运行时间只有0.01秒,比k近邻和逻辑斯蒂少2个数量级。但是,训练效果只有92.5%,明显不如前两者。这说明,训练效果和训练速度是一对矛盾。和人类学习一样,欲速则不达(滑稽~)

3、三种分类算法的比较

到目前为止我们已经了解了k近邻分类、逻辑斯蒂回归和朴素贝叶斯三种分类算法。三种分类算法各有优缺点,比如k近邻的优点是训练效果好,但缺点是时间复杂度高;而朴素贝叶斯刚好相反。下面我们来列一下各种算法在训练效果最好的情况:

分类器运行时间/s训练集正确率测试集正确率
k近邻5.770.9860.987
逻辑斯蒂回归6.741.0000.945
朴素贝叶斯0.020.9280.925

所以说无法直接说那种算法孰优孰劣,只能根据实际问题的规模和性质来决定使用哪种算法。对训练速度要求高的采用朴素贝叶斯,比如手写输入法(当然仅限于拉丁字母和数字);对训练效果要求高的采用k近邻,比如手写扫描仪。

当然,这几种算法比较简单,无法兼顾分类的效率与正确性,所以接下来我们将借助决策树、随机森林等办法来提高。

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值