算法测评 | 机器学习VS深度学习
Posted 机房老哥
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了算法测评 | 机器学习VS深度学习相关的知识,希望对你有一定的参考价值。
OLDER BROTHER
大家好,我是你们的机房老哥!
“机器学习进阶”
「前言」
机器学习和深度学习是很早前就埋下的坑,最近决定整合写一篇,利用机器学习的经典算法朴素贝叶斯和深度学习的经典算法神经网络对鸢尾花数据集进行测试,综合对比两种经典算法的异同。后台回复6月24日获得完整代码。
相关入门教程:
01
二者关系
AI
机
器学习是人工智能的重要子领域,它聚焦于如何构建随着经验而自动改进的算法,可以从数据中提取模式、规律和趋势。为什么叫做「学习」呢?一般编程语言的做法是人类给予指令,机器负责执行。而机器学习则相反,先定义好输出,然后程序自动「学习」出达到目标的「步骤」。机器学习最常见的应用领域:
分类(新闻分类)
预测(股票预测)
聚类(自动分组)
规则提取(数据挖掘)
然而机器学习却并非那么智能,数据和特征决定了机器学习的上限,需要人工不停的调整输入特征,使算法不断逼近该上限,这个过程叫特征工程。在算法一致的情况下,特征工程会极大的影响结果,因此准确率很难达到很高。
深度学习是机器学习的子类,通过模拟人脑的神经元,实现自动提取要素的复杂特征,减去了人工调整的烦恼。
机器学习与深度学习的关系
02
机器学习
Machine Learning,ML
老
哥先用sklearn测试机器学习经典算法之朴素贝叶斯。朴素贝叶斯是一个分类算法,通俗地讲分类算法就是把大量已知特征及类别的样本对象输入计算机,让计算机根据这些已知的类别与特征归纳出类别与特征之间的规律,最终目的是运用得到的分类模型对新输入的对象判断出该对象所属分类。
「朴素」的涵义是假设各特征之间相互独立,这意味着该算法很简单,但是会牺牲一定的准确性。其本质思想是判断输入的条件参数对结果影响的概率,相关公式在一文中已经阐释,这里不再赘述。
from sklearn.datasets import load_iris
import pandas as pd
x_data = load_iris().data
y_data = load_iris().target
x_data = pd.DataFrame(x_data, columns = ['花萼长','花萼宽','花瓣长','花瓣宽'])
x_data['标签'] = y_data
pd.set_option('display.unicode.east_asian_width',True)
print(x_data)
先从sklearn的datasets中导入iris数据集。load_iris().data可以得到数据集的特征项,load_iris().target可以得到标签项。老哥用pandas的DataFrame将数据集变成表格输出,可视化相关参数。pd.set_option()的作用是美化DataFrame的打印效果,使列名与数据对齐。
iris数据集
数据共150个,有4个特征,标签有3类,代表鸢尾花的种类。目标是希望利用朴素贝叶斯算法,实现输入数据后,机器自动判断鸢尾花的种类。
from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(x_data,y_data,test_size=30, random_state=25)
利用train_test_split工具将数据集划分为训练集和测试集。输入特征项x_data、标签项y_data,该工具会自动随机划分数据集。test_size即划分比例,如果是小数,指测试集的百分比,如果是大于1的整数,则指测试集的数据个数,我们这里选择30个数据作为测试集。random_state是随机种子,种子一样,则每次的随机划分结果都一致,否则每一次对数据集的划分都是随机的。
本次为了对比机器学习和深度学习算法,设定随机种子,保证两算法用的数据集划分结果一致。
from sklearn.model_selection import train_test_split
from sklearn.naive_bayes import MultinomialNB
from sklearn.datasets import load_iris
import time
def naviebayes(x_train, x_test, y_train, y_test):
"""
朴素贝叶斯进行鸢尾花分类
"""
mlt = MultinomialNB(alpha=1)
mltStart = time.time()
mlt.fit(x_train, y_train)
mltCostTime = time.time() - mltStart
print("朴素贝叶斯建模%.2f秒" % mltCostTime)
y_score_test = mlt.score(x_test, y_test)
print(y_score_test)
if __name__ == "__main__":
x_data = load_iris().data
y_data = load_iris().target
x_train, x_test, y_train, y_test = train_test_split(x_data, y_data, test_size=30, random_state=25)
naviebayes(x_train, x_test, y_train, y_test)
完整代码如上,为测试特征工程对机器学习结果的影响,先不进行特征工程,直接对数据集进行训练。训练的方法也非常简单,从sklearn.naive_bayes中导入MultinomialNB算法,这里也可以导入GaussianNB(先验为高斯分布的朴素贝叶斯)和BernoulliNB(先验为伯努利分布的朴素贝叶斯),单纯的改个名字即可,大家可以自己测试。
首先将MultinomialNB(alpha=1)实例化为mlt,方便后续调用,参数alpha为拉普拉斯平滑参数,该参数为1代表默认每个特征至少出现一次(避免大量出现0概率的情况)。然后将训练集的x和y传入mlt.fit()中计算,得到模型。将测试集的x和y传入mlt.score()函数中,自动进行预测,并输出准确率。利用time模块计算算法运行时间。
结果可知,在未进行特征工程的情况下,预测准确率为80%。接下来我们测试加入数据预处理后的预测准确率,代码如下:
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import Normalizer
from sklearn.preprocessing import PolynomialFeatures
from sklearn.preprocessing import MinMaxScaler
from sklearn.preprocessing import StandardScaler
from sklearn.preprocessing import OneHotEncoder
from sklearn.preprocessing import Binarizer
from sklearn.naive_bayes import MultinomialNB
from sklearn.datasets import load_iris
import time
def naviebayes(x_train, x_test, y_train, y_test):
for preprocessing_method in [Normalizer(),PolynomialFeatures(),MinMaxScaler(),StandardScaler(),Binarizer(),OneHotEncoder()]:
try:
method = preprocessing_method
method.fit(x_train)
x_train = method.transform(x_train)
x_test = method.transform(x_test)
print('特征工程算法:',str(preprocessing_method).split('(')[0])
mlt = MultinomialNB(alpha=1)
mltStart = time.time()
mlt.fit(x_train, y_train)
mltCostTime = time.time() - mltStart
print("建模时长%.2f秒" % mltCostTime)
y_score_test = mlt.score(x_test, y_test)
print(y_score_test)
except:
print(str(preprocessing_method).split('(')[0],'算法无法在本例使用')
if __name__ == "__main__":
x_data = load_iris().data
y_data = load_iris().target
x_train, x_test, y_train, y_test = train_test_split(x_data, y_data, test_size=30, random_state=25)
naviebayes(x_train, x_test, y_train, y_test)
sklearn将所有预处理算法都封装到了preprocessing函数中,如上述代码所示,老哥用for循环分别测试了六个算法的训练准确率。算法如下:
预处理算法 |
功能 |
Normalizer() 正则化 |
将每个样本缩放到单位范数 |
PolynomialFeatures() 多项式转换 |
构造特征值,例如有 a、b 两个特征,那么它的 2 次多项式为 [1,a,b,a2,ab,b2] |
MinMaxScaler() 区间缩放 |
将属性缩放到指定的极值(通常是1-0)之间 |
StandardScaler() 中心化 |
令所有数据的中心为(0,0) |
Binarizer() 二值化 |
设定一个阈值,大于阈值的赋值为1,小于等于阈值的赋值为0 |
OneHotEncoder() 向量化 |
对离散特征进行独热编码 |
测试结果如下:
正则化和多项式转换降低了预测准确率
区间缩放提高了准确率
中心化因为会生成负数,所以无法在朴素贝叶斯中使用
二值化和独热编码未改变准确率
至此朴素贝叶斯算法的准确率最高为90%,接下来看看神经网络模型。
03
深度学习
Deep Learning, DL
接
下来老哥搭建一个最最简单的,只有一层的神经网络模型,相关介绍请参考。TensorFlow将深度学习算法很好的封装进了Keras,该函数被工业界和学术界广泛使用,仅需20行代码即可搭建一个简单的神经网络。但是这种方法会失去对神经网络的深入理解。因此本案例中,老哥将纯手工实现神经网络所有的数学公式。
如果你能完整的从头到尾跟完本次教程,那么你已经对神经网络有了入门级的理解。老哥会尽可能详细的逐步讲解,那我们开始吧!
import tensorflow as tf
from matplotlib import pyplot as plt
首先导入tensorflow和matplotlib库完成公式搭建和绘图。
x_train = tf.cast(x_train, dtype=tf.float32)
x_test = tf.cast(x_test, dtype=tf.float32)
转换x的数据类型,否则后面矩阵相乘时会因数据类型不一致报错。
train_db = tf.data.Dataset.from_tensor_slices((x_train, y_train)).batch(30)
test_db = tf.data.Dataset.from_tensor_slices((x_test, y_test)).batch(30)
分别将测试集和训练集分批次(Batch),每一批次为32个数据。测试集有120个数据,所以测试集会被分为4批。这4批数据会分批次喂入神经网络结构。分Batch的好处是减少内存的损耗,增加迭代次数有助于实现参数的修正,提高收敛效果。Batch的选择可能影响最终的结果。
w1 = tf.Variable(tf.random.truncated_normal([4, 3], stddev=0.1, seed=1))
b1 = tf.Variable(tf.random.truncated_normal([3], stddev=0.1, seed=1))
生成神经网络的参数,因为有4个输入特征,故输入层为4个输入节点;因为y为3分类,故输出层为3个神经元。用tf.Variable()标记参数可训练,只有标记的数据才会在梯度下降中反向传播。使用truncated_normal随机生成符合截断正态分布随机初始参数w和b。使用seed使每次生成的随机数相同(方便教学,使大家结果和老哥一致,可以不写seed)。打印生成的随机初始值w和b如下:
现在老哥已经搭建出了如下图所示的神经网络结构了。
lr = 0.05
train_loss_results = []
test_acc = []
epoch = 500
loss_all = 0
接下来初始化梯度循环所需的参数。设定学习率lr为0.05;构造空列表train_loss_results,记录每轮的loss损失函数值,为后续画loss曲线提供数据;test_acc同理;循环500轮;因为分了Batch,数据集被划分为4批,因此会生成4个loss,用loss_all记录这4批生成的4个loss的和。
for epoch in range(epoch):
for step, (x_train, y_train) in enumerate(train_db):
with tf.GradientTape() as tape:
y = tf.matmul(x_train, w1) + b1
y = tf.nn.softmax(y)
y_ = tf.one_hot(y_train, depth=3)
loss = tf.reduce_mean(tf.square(y_ - y))
loss_all += loss.numpy()
grads = tape.gradient(loss, [w1, b1])
w1 = w1 - lr * w1_grad b = b - lr * b_grad
* grads[0])
* grads[1])
{}, loss: {}".format(epoch, loss_all/4))
/ 4)
0 =
接下来这段代码用来构造梯度下降,是参数自更新的核心。最外层是数据集级别的循环,每个epoch循环一次数据集,一共循环500轮;第二层是batch级别的循环 ,每个step循环一个batch,一共循环4个batch。循环的内部则是用with结构记录梯度信息,整个神经网络的计算过程图如下:
接下来重点解释神经网络公式的实现。
首先实现公式的前半部分,w和b的乘加运算。tf.matmul()函数可以实现两个矩阵相乘,生成两个矩阵的乘积。
为方便理解,我们将生成的y打印,可以看到我们生成了30行3列的y值,这就是根据上述公式计算出的结果:
接下来用tf.nn.softmax(y)函数使输出得y符合概率分布。运算结果如下所示,每一行的y值相加为1,体现了预测值中每个类别所占的比例。
将标准答案y_train转化为独热编码,方便计算loss和accuracy。
根据均方误差公式,计算标准答案y_train与预测答案y之间的差距,即为loss值。均方误差公式如下,先对每个差距平方,然后求和,最后除以总个数。在代码中,先用tf.square()函数求解y与y_的差的平方,然后用tf.reduce_mean()求解均值。
loss值计算如下:
现在我们得到了每一个预测值与标准值之间的均方差,接下来通过梯度下降的方式,找到使loss值最小的w和b参数。梯度下降公式如下。我们先利用tape.gradient()函数计算每一个tape中的loss对各个参数的梯度(导数)。然后用assign_sub()函数实现w和b参数的自减,从而实现参数的自更新:w1 = w1 - lr * w1_grad b = b - lr * b_grad。
公式写完后,将将4个step的loss求平均记录在train_loss_results中,并将loss_all归零,为记录下一个epoch的loss做准备。
通过上述操作,我们已经纯手工写完了所有神经网络的计算公式。接下来,我们用测试集来测试该训练结果的准确度。
total_number = 0, 0
for x_test, y_test in test_db:
y = tf.matmul(x_test, w1) + b1
y = tf.nn.softmax(y)
pred = tf.argmax(y, axis=1)
pred = tf.cast(pred, dtype=y_test.dtype)
correct = tf.cast(tf.equal(pred, y_test), dtype=tf.int32)
correct = tf.reduce_sum(correct)
total_correct += int(correct)
total_number += x_test.shape[0]
acc = total_correct / total_number
test_acc.append(acc)
", acc) :
total_correct为预测对的样本个数, total_number为测试的总样本数,将这两个变量都初始化为0。使用更新后的w,b参数对测试集进行预测。最开始两步的计算和softmax刚才已经解释过了。这样我们得到了预测结果y。tf.argmax()函数可以得到预测结果中得分最高的结果的索引值,而索引值等同于分类结果。预测的分类结果如下图所示:
将预测结果pred转换为y_test的数据类型。用tf.equal()函数判断预测值与标准答案是否一致,一致则输出True,不一致则为False。利用tf.cast()函数将布尔值转化为0,1的整数形式。因为分了batch,所以要将每一个batch的判断正确的数量相加,得到总的正确数量。则总的准确率等于total_correct / total_number。这样我们就可以得到预测准确率了。
至此,整个神经网络模型的代码就写完了。
plt.title('Loss Function Curve') # 图片标题
plt.xlabel('Epoch') # x轴变量名称
plt.ylabel('Loss') # y轴变量名称
plt.plot(train_loss_results, label="$Loss$") # 逐点画出trian_loss_results值并连线,连线图标是Loss
plt.legend() # 画出曲线图标
plt.show() # 画出图像
plt.title('Acc Curve') # 图片标题
plt.xlabel('Epoch') # x轴变量名称
plt.ylabel('Acc') # y轴变量名称
plt.plot(test_acc, label="$Accuracy$") # 逐点画出test_acc值并连线,连线图标是Accuracy
plt.legend()
plt.show()
最后我们用折线图将损失函数和准确率绘制出来,进行可视化。如下所示,loss函数稳定减小,acc准确率稳步上升,最后达到了93.333%的准确率。
调整学习率和迭代次数,将学习率改为0.04,迭代次数增加到1000,改进后准确率达到了96.66%。
综上所述,本次对比了机器学习与深度学习计算的整个过程,可以看出深度学习的准确率相对更高,但是耗时较长。如果数据量更大,深度学习的效果会更好。本次测评到此结束,完整代码回复6月24日即可获得。
- END -
关注老哥,一起充电!
机房老哥
欢迎扫码关注!
以上是关于算法测评 | 机器学习VS深度学习的主要内容,如果未能解决你的问题,请参考以下文章
机器学习 vs 深度学习到底有啥区别,为什么更多人选择机器学习