MNIST数据集,图像识别(一)

1、MNIST简介

        MNIST是一组带标签的图像集合,专门为监督学习组装而成,是改良的NIST。官网有这样的一段描述性话语:“ It is a subset of a larger set available from NIST. The digits have been size-normalized and centered in a fixed-size image.” MNIST包含了若干手写的数字图像,其数值也是该图像的标签。每个图像的像素是28px*28px的灰度像素。

       该数据集目前有 60,000 个示例的训练集和10,000 个示例的测试集,按照从0到9的顺序被整齐地划分为7000组样本,数字是从实际问题中采集的真实手写数字,所以它们具有多样性。MNIST将图像和标签存储在单独的文件中,我们可以编写简单的程序读取它们。

        下载地址:    MNIST handwritten digit database, Yann LeCun, Corinna Cortes and Chris Burgeshttp://yann.lecun.com/exdb/mnist/

2、训练和测试 

        我们通过带标签的样本数据训练模型,使用样本计算出的预测值与实际结果进行比较。如果两组数值越接近,则说明预测的效果就越好,但是这种方法可能会存在一些问题,比如最大的问题可能是过拟合(overfitting),其意思就是模型的训练误差远小于它在测试数据集上的误差,当然这个问题,可以采用一些方法去减少其影响。

        注意:千万不能使用训练系统时使用过的样本数据来测试系统,这样得到的结果是不理想的。

        我们可以使用两个矩阵X,Y去分别表示样本数据和标签值,这也是二元分类的思想。

        X中的每行元素代表一个样本数据,每列元素代表一个输入变量,其中有一列是偏置列。为了使MNIST中的图像数据符合这种格式,必须对其进行格式化为一行像素,这样每个像素就成为一个输入变量。MNIST图像有28*28个像素,因此会产生784个元素的行,再加上偏置列,每个图像样本的数据是一个包含785个元素的行。所以,X就是60,000行和785列组成的矩阵。记住,每一行是一幅图像。

        对于矩阵Y 依据二元分类器的思想,它应该只包含0和1,而我们的数据集里的标签则是从0-9的,那么,我们采用缩小范围的方式,以某一个数字为界限,比如中间的数字5,所以我们将0-9的标签分成两类——“是5”和“不是5”。

2、加载图像

         我们使用python编写代码,如下:

import numpy as np
import struct
import gzip


# 加载图像
def load_images(filename):
    # 打开并解压文件
    with gzip.open(filename, 'rb') as f:
        # 定义变量存储文件里的标题信息,struct.unpack()函数是根据模式字符串从二进制文件中读取数据
        _ignored, n_images, columns, rows = struct.unpack('>IIII', f.read(16))
        # 往Numpy的字节数组中读取所有的像素
        all_pixels = np.frombuffer(f.read(), dtype=np.uint8)
        # 将像素重塑为一个矩阵,其每一行都是一个图像,并返回
        return all_pixels.reshape(n_images, columns * rows)


def prepend_bias(X):
    # 追加一个偏置列,且元素均是1
    return np.insert(X, 0, 1, axis=1)


# 将训练图像样本的60000*785矩阵保存到变量中,并初始化原始数据
X_train = prepend_bias(load_images("../data/mnist/train-images-idx3-ubyte.gz"))

# 存储测试图像样本
X_test = prepend_bias(load_images("../data/mnist/t10k-images-idx3-ubyte.gz"))

3、加载标签

        我们使用python编写代码,与加载图像的代码放在一起,命名为mnist.py,如下:

# 加载标签
def load_labels(filename):
    with gzip.open(filename, 'rb') as f:
        # 跳过标题字节
        f.read(8)
        # 将所有的标签放入一个列表
        all_labels = f.read()
        # 将标签列表重塑为一列的矩阵
        return np.frombuffer(all_labels, dtype=np.uint8).reshape(-1, 1)


def encode_fives(Y):
    # 创建一个数组,Y包含5时为真,不包含5时为假,并将该数组转换为整数数据,所有为真的值就变成了1,为假则是0
    return (Y == 5).astype(int)


# 训练标签,如果数字是5则为1,否则0
Y_train = encode_fives(load_labels("../data/mnist/train-labels-idx1-ubyte.gz"))

# 测试标签,如果数字是5则为1,否则0
Y_test = encode_fives(load_labels("../data/mnist/t10k-labels-idx1-ubyte.gz"))

4、模型训练与测试

        采用分类算法进行训练模型。

import numpy as np
import mnist as mi


# S型函数,平滑误差
def sigmoid(z):
    return 1 / (1 + np.exp(-z))


def forward(X, w):
    weighted_sum = np.matmul(X, w)
    return sigmoid(weighted_sum)


def classify(X, w):
    return np.round(forward(X, w))


# log损失函数
def loss(X, Y, w):
    y_hat = forward(X, w)
    first_term = Y * np.log(y_hat)
    second_term = (1 - Y) * np.log(1 - y_hat)
    return -np.average(first_term + second_term)


# 梯度下降法
def gradient(X, Y, w):
    return np.matmul(X.T, (forward(X, w) - Y)) / X.shape[0]


# iterations为迭代次数
def train(X, Y, iterations, lr):
    w = np.zeros((X.shape[1], 1)) # 初始化权重w
    for i in range(iterations):
        print("Iteration %4d => Loss: %.20f" % (i, loss(X, Y, w)))
        w -= gradient(X, Y, w) * lr
    return w


def test(X, Y, w):
    total_examples = X.shape[0]
    correct_results = np.sum(classify(X, w) == Y)
    success_percent = correct_results * 100 / total_examples
    print("\nSuccess: %d/%d (%.2f%%)" %
          (correct_results, total_examples, success_percent))


# 训练模型
w = train(mi.X_train, mi.Y_train, iterations=100, lr=1e-5)
# 模型测试
test(mi.X_test, mi.Y_test, w)

        iterations和lr(学习率) 也是此次模型中的超参数(也就是认为设定的值),进行反复的调整,找到一个最优的参数,调参也是机器学习的基本工作之一。

        现在,我们将程序运行一下,看看模型预测的效果如何!

         我们发现,迭代了99次,有超过96%的样本预测被证明是准确的,有一个直观上的感觉,但这个预测系统是否就一定准确的,答案是否定的。不过这也是一种不错的处理方法!

        注意:我们的程序是只识别数字5的。

参考文献:

Programming Machine Learning: Form Coding to Deep Learning.[M],Paolo Perrotta,2021.6. 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值