【机器学习入门】识别手写数字——感知机

系列文章目录

第1章 专家系统
第2章 决策树
第3章 神经元和感知机



数据来源及说明

MNIST数据集是美国国家标准与技术研究院的比较常用的一个手写数字数据集。
而scikit-learn软件包中提供的是加州大学尔湾分校(UCI) 的数据集。
UCI的手写数据集包含1797张 8 * 8 像素灰度图片, 每个图片都是一张手写的数字。

为了使图片能够作为感知机的输入,我们把每张图片展开成长度为64的向量。向量的值表示像素的灰度,通常我们会把数值归一化到 [0,1] 这个区间,防止过大的值给感知机学习权重带来不必要的难度。较大的输入值通常意味着需要较大的权重。同时在训练感知机的过程中,权重的更新量也受到输入大小的影响,过大的输入可能会使权值更新幅度过大,造成模型不稳定。

代码操作

先来预览一下数据集中的图片

import matplotlib.pyplot as plt
from sklearn.datasets import load_digits

# 加载UCI手写数字数据集
digits = load_digits()
for i in range(10):
    # 显示1行10列中的第 i+1 个图
    plt.subplot(1,10,i+1)
    plt.imshow(digits.images[i])
    # 隐藏坐标轴刻度
    plt.axis('off')
plt.show()

在这里插入图片描述
我们将图像的每个像素点作为一个输入维度,因此,分类手写数字这个任务的输入维度比较高。但是每两个数字之间在一些特定区域都有显著的不同之处,在所有像素点构成的高维空间里(对于 UCI数据集来说是64维空间),这些数字其实分布在几乎 线性可分 的不同区域。所以,分类手写数字这个任务恰好落在感知机的能力范围之内。
单个感知机的输出通常可以用来区分两个类别:超过某阈值表明输入样本属于类别A,低于该阈值则表明属于类别B。通常,我们不会采用单一输出来区分更多类别,即使激活函数的输出值是连续的,也通常仅用来表示两种状态,因为输出值通常集中于0或者1附近,0和1的中间部分不具有很好区分更多类别的解析度。而且,将类别嵌入连续值表示的区间上,隐含了类别之间的顺序关系,然而这种强加的顺序关系通常会给模型的训练徒增难度
我们采用10个感知机分别识别10个不同的数字。每个感知机的输出用于区分是或者不是某个数字。如果输入图像包含某个数字i,那么,我们希望第i个感知机的输出接近1,而其他感知机的输出接近0。当几个感知机对同一个输入都给出接近1的输出时,我们选取输出值最大的那个感知机所代表的数字。
读者可以尝试采用我们前面实现的感知机,为每个数字建立一个感知机进行训练。这里,我们介绍使用scikit-learn中的感知机模型,因为该模型能够简化我们的操作。在scikit-learn 软件包中,感知机Perceptron的训练函数fit输入的目标值y并不是单个感知机输出的目标值,而是样本的类别。这个类别可以是数值,也可以是字符串标签。模型会根据输入y中不同值的数量,确定不同
类别的个数,从而分别建立感知机。对于手写数字识别的实验,模型会建立10个感知机,用来构造和训练10组权值。

from sklearn.linear_model import  Perceptron
from sklearn.datasets import load_digits


# X 是1997行, 64列 的矩阵
# 每一行是一个手写数字样本
# 图像中的像素被拉平 展开为长度为64的行向量
# y是长度为1797 的数组,包含样本对应的数字值
X,y = load_digits(return_X_y = True)

# 创建感知机模型
# max_iter 是最大迭代次数
# tol参数可以控制当误差不再减小时,提前结束训练
# 当本轮误差 减去 前一轮误差 的差值 大于tol时,结束训练
# eta0 是学习率
perceptron = Perceptron(max_iter=1000, tol=0.0001, eta0=1)

# 与我们实现的感知机模型的训练方法略有不同
# 这里 y值表示样本的类别
# 根据 y中 不同值的数量(也就是类别数量) 分别建立若干感知机
perceptron.fit(X,y)

当感知机训练完成后,我们把每个像素点对应的权值重新按照像素点位置排列起来,将权值大小转换为颜色(权值越小颜色越偏向红色,权值越大颜色越偏向蓝色,中间值为白色),就可以观察到感知机是如何工作的了。__在scikit-learn软件包训练出的感知机中,coef_属性包含了感知机的权值向量。__当分类数量为2时,只需要一个感知机,那么该属性包含一个向量。当分类数量大于2时,该属性为矩阵,矩阵的行数即类别的数量,每一行为该类别对应的感知机权值向量。我们用手写数字数据集训练出的模型的权值矩阵包含10行,这说明我们训练得到了10个感知机。将这些感知机的权值画成图像,可以明显地看到,权值的分布大体反映了数字笔画的平均走向,我们依稀可以从中看出数字的轮廓,如图3.8所示。

import numpy  as np
import matplotlib.pyplot as plt

for i in range(10):
    # 显示 1 行 10 列 中的第i + 1个图
    plt.subplot(1,10,i+1)
    # 显示第 i 个类别对应的感知机权值
    # 将权值向量 整形为矩阵和输入图像像素位置对应
    # 使用红蓝颜色表RdBu, 红色表示负值, 蓝色表示正值
    plt.imshow(np.reshape(perceptron.coef_[i,:],(8,8)), cmap=plt.cm.RdBu)
    # 隐藏坐标轴刻度
    plt.axis('off')
plt.show()

在这里插入图片描述
接下来,我们取数据集的前10个样本,观察感知机对应的输出。
我们通过coef_ 和 intercept_属性得到感知机的权值和偏置,用矩阵乘法批量计算出10个样本对应的感知机输出,每行表示一个感知机,每列表示一个样本。
由于感知机的输出过于接近0 或 1, 为了显示出它们接近 0 或 1 的程度上的细微差别,我们对输出进行缩放。
我们采用 σ ( t ) = ( 1 + e − t / 1000 ) − 1 \sigma(t)=(1+e^{-t/1000})^{-1} σ(t)=(1+et/1000)1作为激活函数,这是一个横向拉伸了1000倍的Sigmoid函数,可以更加明显地观察到输出值的差异。

import numpy as np
# 取数据集的前10个样本验证感知机的输出
# 感知机的权值,每行表示一个感知机的权值
w = perceptron.coef_
# 感知机的偏置, 列向量, 每行表示一个感知机的偏置
b = perceptron.intercept_
# 对数据集的前10个样本, 计算每个感知机的输出
# 将数据转置, 每列表示一个样本
x = np.transpose(X[0:10,:])
out = np.matmul(w,x) + b
# 由于输出太接近0 或者 1, 我们进行缩放, 以显示差异
out = 1/(1+np.exp(-out/1000))
for i in range(10):
    print(list(map(lambda x: '{0:.2f}'.format(x),out[i,:])))

这里用AI解释了一下最后一个打印语句:
这行代码是Python中的一个打印语句,它使用了map函数和一个匿名的lambda函数来格式化out数组中每一行的输出值。具体来说,这个语句做了以下几件事情:

out[i, :]:这是一个NumPy数组索引操作,它选择了out数组中的第i行的所有元素。out数组是在前面的代码中计算得到的,包含了使用感知机模型对前10个样本进行预测后得到的输出值。

map(lambda x: ‘{0:.2f}’.format(x), out[i, :]):map函数接受一个函数和一个迭代器作为参数,并将这个函数应用到迭代器中的每个元素上。这里的函数是一个lambda匿名函数,它接受一个参数x(代表out数组中的一个输出值),并返回一个格式化后的字符串。‘{0:.2f}’.format(x)是一个字符串格式化表达式,它将数值x格式化为保留两位小数的浮点数字符串。

list(…):map函数返回的是一个迭代器对象,而list()函数将这个迭代器转换成了一个列表。这样做是为了将所有格式化后的字符串收集到一个列表中。

最终,这行代码打印出一个列表,列表中的每个元素都是一个字符串,代表out数组第i行中的一个输出值,格式化为保留两位小数的浮点数。这样的格式化有助于清晰地展示数值,特别是当数值很多或者需要精确到特定小数位数时。
在这里插入图片描述
对角线输出值较大,说明对应的感知机识别出了图片中的数字。但数字5倍误认为数字1。
对于上面的矩阵运算有点晕,不明白为什么每一列代表的是样本。然后写了一下这个过程。
在这里插入图片描述


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值