【深度学习】基于MindSpore和pytorch的Softmax回归及前馈神经网络

news2024/12/29 10:12:41

1 实验内容简介

1.1 实验目的

(1)熟练掌握tensor相关各种操作;

(2)掌握广义线性回归模型(logistic模型、sofmax模型)、前馈神经网络模型的原理;

(3)熟练掌握基于mindspore和pytorch的广义线性模型与前馈神经网络模型的实现。

 

1.2 实验内容及要求

请基于mindspore和pytorch平台实现对MNIST数据集的分类分析,并以分类的准确度和混淆矩阵为衡量指标,分析二个模型(softmax、前馈神经网络)的精度。

要求:pytorch可与tensorflow替换,但mindspore为必选平台,建议安装1.5版本。(mindspore可以在华为云ModelArts上实现)。

 

1.3 实验数据集介绍

1.3.1 数据集简介

MNIST数据集(Mixed National Institute of Standards and Technology Database)是一个用来训练各种图像处理系统的二进制图像数据集,广泛应用于机器学习中的训练和测试。MNIST数据集共有70000张图像,其中训练集60000张,测试集10000张。所有图像都是28×28的灰度图像,每张图像包含一个手写数字。

 

1.3.2 数据集详细信息

(1)数据量

训练集60000张图像,其中30000张来自NIST的Special Database 3,30000张来自NIST的Special Database 1。测试集10000张图像,其中5000张来自NIST的Special Database 3,5000张来自NIST的Special Database 1。

(2)标注情况

每张图像都有标注。

(3)标注类别

共10个类别,每个类别代表0~9之间的一个数字,每张图像只有一个类别。

 

1.3.3 数据集文件结构

(1)目录结构

·解压前

dataset_compressed/

├── t10k-images-idx3-ubyte.gz        #测试集图像压缩包(1648877 bytes)

├── t10k-labels-idx1-ubyte.gz         #测试集标签压缩包(4542 bytes)

├── train-images-idx3-ubyte.gz        #训练集图像压缩包(9912422 bytes)

└── train-labels-idx1-ubyte.gz         #训练集标签压缩包(28881 bytes)

·解压后

dataset_uncompressed/

├── t10k-images-idx3-ubyte                #测试集图像数据

├── t10k-labels-idx1-ubyte                 #测试集标签数据

├── train-images-idx3-ubyte                #训练集图像数据

└── train-labels-idx1-ubyte                 #训练集标签数据

 

(2)文件结构

MNIST数据集将图像和标签都以矩阵的形式存储于一种称为idx格式的二进制文件中。该数据集的4个二进制文件的存储格式分别如下:

·训练集标签数据 (train-labels-idx1-ubyte)

58c8bb751d23430c904fe1fd1575e38b.png

·训练集图像数据(train-images-idx3-ubyte)

 f336dec8f7db4d50b5ca99249e73507d.png

·测试集标签数据(t10k-labels-idx1-ubyte) 

06cd34d3682a4e35badbc6d9649be678.png

·测试集图像数据 (t10k-images-idx3-ubyte) 

e9ca79de815e4c749a89563d7fe9a8be.png

 

2 算法原理阐述

2.1 Softmax回归

Softmax 回归模型主要用于解决离散值预测的多分类问题,是Logistic回归在多分类问题上的推广。Softmax回归和Logistic回归一样,也是将输入特征与权重做线性叠加,但是Softmax回归的输出值个数等于标签中的类别数,对每个输入计算输出。譬如我们考察一个如下图所示的隐层有四个结点、输出层有三个结点的单隐层神经网络,每个输出的计算依赖于所有的输入。

 df9a19d0643d41c5a399841ab9b5b85e.png

softmax函数又称归一化指数函数,它是二分类函数sigmoid在多分类上的推广,目的是将多分类的结果以概率的形式展现出来。下图展示了softmax的计算方法:

92eef6b7d8a4458c90bf3a3f17ea4db4.png

softmax第一步就是将模型的预测结果转化到指数函数上,这样保证了概率的非负性。为了确保各个预测结果的概率之和等于1,我们需要将转换后的结果进行归一化处理,方法就是将转化后的结果除以所有转化后结果之和,这样就得到近似的概率。

 

2.2 前馈神经网络

前馈神经网络中,把每个神经元按接收信息的先后分为不同的组,每一组可以看做是一个神经层。每一层中的神经元接收前一层神经元的输出,并输出到下一层神经元。整个网络中的信息是朝着一个方向传播的,没有反向的信息传播(和误差逆传播算法不是一回事),可以用一个有向无环图来表示。前馈神经网络包括全连接前馈神经网络和卷积神经网络。前馈神经网络可以看做是一个函数,通过简单非线性函数的多次复合,实现输入空间到输出空间的复杂映射。多层前馈神经网络的图示如下:

8bba1c9287184b7fb211d7d8944413d4.png

使用随机梯度下降的误差反向传播算法的具体训练过程伪代码描述如下:

46c7603d964d49e88504ecd0a6e51a1d.png

 

3 实验流程及代码实现

3.1 实验平台简介

3.1.1 MindSpore

MindSpore是华为公司自研的最佳匹配昇腾AI处理器算力的全场景深度学习框架,为数据科学家和算法工程师提供设计友好、运行高效的开发体验,推动人工智能软硬件应用生态繁荣发展,目前MindSpore支持在EulerOS、Ubuntu、Windows系统上安装。

 

3.1.2 pytorch

PyTorch是一个开源的Python机器学习库,基于Torch,用于自然语言处理等应用程序。2017年1月,由Facebook人工智能研究院(FAIR)基于Torch推出了PyTorch。它是一个基于Python的可续计算包,提供两个高级功能:1、具有强大的GPU加速的张量计算(如NumPy)。2、包含自动求导系统的深度神经网络。

 

3.2 评价指标

3.2.1 混淆矩阵

混淆矩阵(Confusion Matrix)又被称为错误矩阵,通过它可以直观地观察到算法的效果。它的每一列是样本的预测分类,每一行是样本的真实分类(反过来也可以),顾名思义,它反映了分类结果的混淆程度。

 0d69220e826e403da8e229dc94814d5f.png

·P(Positive):代表1,表示预测为正样本;

·N(Negative):代表0,表示预测为负样本;

·T(True):代表预测正确;

·F(False):代表预测错误。

下列Positive和Negative表示模型对样本预测的结果是正样本(正例)还是负样本(负例)。True和False表示预测的结果和真实结果是否相同。

·True positives(TP)

预测为1,预测正确,即实际为1;      

·False positives(FP) 

预测为1,预测错误,即实际为0;

·False negatives(FN)

预测为0,预测错误,即实际为1;

·True negatives(TN)

预测为0,预测正确,即实际为0。

 

3.2.2 准确率

准确率(Accuracy)衡量的是分类正确的比例。

 

 

3.3 实验流程

3.3.1 基于MindSporeSoftmax回归

3.3.1.1 读取数据集

分别读取MNIST的标签数据和图像数据。由前面的数据集介绍可知,标签数据的前8个字节是magic number和样本个数字段,所以标签数据的偏移量为8。我们使用struct.unpack方法读取前两个数据,lbpath.read(8)表示一次从文件中读取8个字节,这样读到的前两个数据分别是magic number(2049)和样本个数(60000),之后再读取标签数据。同样地,图像数据的前16个字节分别是magic number、图像数量、图像的高rows和图像的宽columns。我们使用struct.unpack方法读取前四个数据,lbpath.read(16)表示一次从文件中读取16个字节,这样读到的前四个数据分别是magic number(2051)、图像数量(60000)、图像的高rows(28)和图像的宽columns(28)。

# 导入已下载的数据集
def load_mnist(path, kind='train'):
    # os.path.join()函数用于路径拼接文件路径
    labels_path = os.path.join(path, '%s-labels.idx1-ubyte' % kind)
    images_path = os.path.join(path, '%s-images.idx3-ubyte' % kind)

    # 读取训练集标签数据集
    with open(labels_path, 'rb') as lbpath:
        magic, n = struct.unpack('>II',lbpath.read(8))
        # 使用struct.unpack方法读取前两个数据。lbpath.read(8)表示一次从文件中读取8个字节
        # 这样读到的前两个数据分别是magic number(2049)和样本个数(60000)
        labels = np.fromfile(lbpath,dtype=np.uint8)
        # 读取标签,标签的数值在0~9之间

    # 读取训练集图片数据集
    with open(images_path, 'rb') as imgpath:
        magic, num, rows, cols = struct.unpack('>IIII',imgpath.read(16))
        # 使用struct.unpack方法读取前四个数据。lbpath.read(16)表示一次从文件中读取16个字节
        # 这样读到的前四个数据分别是magic number(2051)、图像数量(60000)、图像的高rows(28)、图像的宽columns(28)
        images = np.fromfile(imgpath,dtype=np.uint8).reshape(len(labels), 28, 28, 1)  # 设置图像形状,高度宽度均为28,通道数为1
    return images, labels
    # labels形状为(60000,)
    # images形状为(60000, 28, 28, 1)

3.3.1.2 自定义迭代器

mindspore.dataset提供了部分常用数据集和标准格式数据集的加载接口。对于MindSpore暂不支持直接加载的数据集,可以通过构造自定义数据集类或自定义数据集生成函数的方式来生成数据集,然后通过mindspore.dataset.GenaratorDataset接口实现自定义方式的数据集加载。通过自定义数据集类和自定义数据集生成函数两种方式生成的数据集,都可以完成加载、迭代等操作。由于在自定义数据集类中定义了随机访问函数和获取数据集大小函数,因此当需要随机访问数据集中某条数据或获取数据集大小时,使用自定义数据集类生成的数据集可以快速完成这些操作,而通过自定义数据集生成函数的方式生成的数据集需要对数据逐条遍历方可完成这些操作。一般情况下,当数据量较小时使用两种生成自定义数据集的方式中的任一种都可以,而当数据量过大时,优先使用自定义数据集类的方式生成数据集。

在用户自定义数据集类中须要自定义的类函数如下:

·__init__:定义数据初始化等操作,在实例化数据集对象时被调用。

·__getitem__:定义该函数后可使其支持随机访问,能够根据给定的索引值index,获取数据集中的数据并返回。数据返回值类型是由NumPy数组组成的Tuple。

·__len__:返回数据集的样本数量。

在完成自定义数据集类之后,可以通过GeneratorDataset接口按照用户定义的方式加载并访问数据集样本。下面我们通过两段示例代码来说明使用自定义数据集类的方式生成单标签数据集和多标签数据集的方法。

class FashionMnist():
    def __init__(self, path, kind):  # 定义数据初始化等操作,在实例化数据集对象时被调用
        self.data, self.label = load_mnist(path, kind)

    def __getitem__(self, index):  # 定义该函数后可使其支持随机访问,能够根据给定的索引值index,获取数据集中的数据并返回
        return self.data[index], self.label[index]

    def __len__(self):  # 返回数据集的样本数量
        return len(self.data)

 

3.3.1.3 数据归一化

# 数据变换
trans = [cv.Rescale(1.0 / 255.0, 0), cv.HWC2CHW()] # 数据做标准化处理,所得到的数值分布满足正态分布
# 调整图像的像素大小。Rescale变换用于调整图像像素值的大小,包括两个参数:
# rescale:缩放因子。shift:平移因子。图像的每个像素将根据这两个参数
# 进行调整,输出的像素值为 outputi = inputi ∗ rescale+shift
# HWC2CWH变换用于转换图像格式,(height, width, channel)转为(channel, height, width)
type_cast_op = C.TypeCast(mindspore.int32)  # 将输入的Tensor转换为指定的数据类型
if resize:
    trans.insert(0, cv.Resize(resize))  # 调整为给定的尺寸大小
mnist_train = mnist_train.map(trans, input_columns=["image"])
mnist_test = mnist_test.map(trans, input_columns=["image"])
mnist_train = mnist_train.map(type_cast_op, input_columns=['label'])
mnist_test = mnist_test.map(type_cast_op, input_columns=['label'])

mnist_train = mnist_train.batch(batch_size, num_parallel_workers=works)
mnist_test = mnist_test.batch(batch_size, num_parallel_workers=works)

 

3.3.1.4 构建网络

nn.SequentialCell是一个有序的Cell容器,输入Tensor将按照定义的顺序通过所有Cell。我们可以使用SequentialCell来快速组合构造一个神经网络模型。nn.Flatten()将输入的X维度从[256,1,28,28]变成[256,784],则一个样本数据一行。损失函数使用SoftmaxCrossEntropyWithLogits交叉熵损失函数,同时计算softmax及其损失。优化器采用随机梯度下降SGD,学习率指定为0.1。

net = nn.SequentialCell([nn.Flatten(), nn.Dense(784, 10, weight_init=Normal(0.01, 0), bias_init='zero')])
loss = nn.SoftmaxCrossEntropyWithLogits(sparse=True, reduction='mean')
optim = nn.SGD(net.trainable_params(), learning_rate=0.1)

 

3.3.1.5 训练网络

# 训练模型一个迭代周期
def train_epoch(net, train_iter, loss, optim):
    net_with_loss = nn.WithLossCell(net, loss)                # 将net与loss连接
    net_train = nn.TrainOneStepCell(net_with_loss, optim)     # 将net,loss,optim连接,生成训练模型
    metric = Accumulator(3)
    for X, y in train_iter:
        l = net_train(X, y)
        y_hat = net(X)
        metric.add(float(l.sum().asnumpy()),accuracy(y_hat, y), y.size)
    return  metric[0] / metric[2], metric[1] / metric[2]

 3.3.1.6 准确率及混淆矩阵

# 计算在指定数据集上模型的精度;得到混淆矩阵
def evaluate_accuracy(net, data_iter):
    metric = Accumulator(2)         # 累加器,metric[0]记录正确预测数,metric[1]记录预测总数
    hunxiao=np.zeros((10,10))
    for X, y in data_iter:
        metric.add(accuracy(net(X), y), y.size)
        y_hat = net(X).argmax(axis=1)
        hunxiao+=confusion_matrix(y.asnumpy(),y_hat.asnumpy())
    plot_confusion_matrix(hunxiao,  title='Confusion Matrix')
    return metric[0] / metric[1]    # 正确预测数 / 预测总数

 

3.3.1.7 绘制混淆矩阵

classes = ['0', '1', '2', '3', '4', '5','6','7','8','9']
def plot_confusion_matrix(cm, title='Confusion Matrix'):
    plt.figure(figsize=(12, 8), dpi=100)
    np.set_printoptions(precision=2)
    # 混淆矩阵中每格的值
    ind_array = np.arange(len(classes))
    x, y = np.meshgrid(ind_array, ind_array)
    for x_val, y_val in zip(x.flatten(), y.flatten()):
        c = cm[y_val][x_val]
        if c > 0.001:
            plt.text(x_val, y_val, "%0.2f" % (c,), color='#EE3B3B', fontsize=10, va='center', ha='center')
    plt.imshow(cm, interpolation='nearest', cmap=plt.cm.binary)
    plt.title(title)
    plt.colorbar()
    xlocations = np.array(range(len(classes)))
    plt.xticks(xlocations, classes, rotation=90)
    plt.yticks(xlocations, classes)
    plt.ylabel('Actual Label')
    plt.xlabel('Predict Label')
    tick_marks = np.array(range(len(classes))) + 0.5
    plt.gca().set_xticks(tick_marks, minor=True)
    plt.gca().set_yticks(tick_marks, minor=True)
    plt.gca().xaxis.set_ticks_position('none')
    plt.gca().yaxis.set_ticks_position('none')
    plt.grid(True, which='minor', linestyle='-')
    plt.gcf().subplots_adjust(bottom=0.15)
    plt.show()

 

3.3.2 基于pytorchSoftmax回归

3.3.2.1 定义网络结构

网络结构采用784个输入结点和10个输出结点,激活函数采用softmax。

class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()  # 初始化
        self.fc1 = nn.Linear(784, 10)  # 784个输入10个输出
        self.softmax = nn.Softmax(dim=1)  # 激活函数 dim=1表示对第一个维度进行概率计算

    def forward(self, x):
        # torch.Size([64, 1, 28, 28]) -> (64,784)
        x = x.view(x.size()[0], -1)  # 4维变2维 (在全连接层做计算只能2维)
        x = self.fc1(x)  # 传给全连接层继续计算
        x = self.softmax(x)  # 使用softmax激活函数进行计算
        return x

 

3.3.2.2 训练模型 

def train():
    for i, data in enumerate(train_loader):
        # 获得一个批次的数据和标签
        inputs, labels = data
        # 获得模型预测结果(64,10)
        out = model(inputs)
        # to onehot 把数据标签变成独热编码
        labels = labels.reshape(-1, 1)  # 先把1维变成2维(64)-(64,1)
        # tensor.scatter(dim,index,src)
        # dim:对那个维度进行独热编码
        # index:要将src中对应的值放到tensor那个位置
        # src:插入index的数值
        one_hot = torch.zeros(inputs.shape[0], 10).scatter(1, labels, 1)
        # 计算loss   mse_loss的两个数据的shape要一致
        loss = mse_loss(out, one_hot)
        # 梯度清零
        optimizer.zero_grad()
        # 计算梯度
        loss.backward()
        # 修改权值
        optimizer.step()

 

3.3.3 基于MindSpore的前馈神经网络

3.3.3.1 加载并查看数据集

MNIST是一个手写数字数据集,训练集包含60000张手写数字,测试集包含10000张手写数字,共10类。可在MNIST数据集的官网下载数据集,解压到当前代码目录下。MindSpore的dataset模块有专门用于读取和解析Mnist数据集的源数据集,可直接读取并生成训练集和测试集。

ds_train = ds.MnistDataset(os.path.join(r'D:\Dataset\MNIST', "train"))
ds_test = ds.MnistDataset(os.path.join(r'D:\Dataset\MNIST', "test"))

print('训练数据集数量:', ds_train.get_dataset_size())
print('测试数据集数量:', ds_test.get_dataset_size())
# 该数据集可以通过create_dict_iterator()转换为迭代器形式,然后通过get_next()一个个输出样本
image = ds_train.create_dict_iterator().get_next()

print('图像长/宽/通道数:', image['image'].shape)
# 一共10类,用0-9的数字表达类别。
print('一张图像的标签样式:', image['label'])

 

3.3.3.2 生成测试集和训练集

创建数据集,为训练集设定Batch Size,这是因为我们通常会采用小批量梯度下降法(MBGD)来训练网络,所以batch size作为一个非常重要的超参数需要提前设定好。在本代码中,batch size为128,意味着每一次更新参数,我们都用128个样本的平均损失值来进行更新。 

def create_dataset(training=True, batch_size=128, resize=(28, 28), rescale=1 / 255, shift=-0.5, buffer_size=64):
    ds = ms.dataset.MnistDataset(DATA_DIR_TRAIN if training else DATA_DIR_TEST)

    # 定义改变形状、归一化和更改图片维度的操作。
    # 改为(28,28)的形状
    resize_op = CV.Resize(resize)
    # rescale方法可以对数据集进行归一化和标准化操作,这里就是将像素值归一到0和1之间,shift参数可以让值域偏移至-0.5和0.5之间
    rescale_op = CV.Rescale(rescale, shift)
    # 由高度、宽度、深度改为深度、高度、宽度
    hwc2chw_op = CV.HWC2CHW()

    # 利用map操作对原数据集进行调整
    ds = ds.map(input_columns="image", operations=[resize_op, rescale_op, hwc2chw_op])
    ds = ds.map(input_columns="label", operations=C.TypeCast(ms.int32))
    # 设定洗牌缓冲区的大小,从一定程度上控制打乱操作的混乱程度
    ds = ds.shuffle(buffer_size=buffer_size)
    # 设定数据集的batch_size大小,并丢弃剩余的样本
    ds = ds.batch(batch_size, drop_remainder=True)
    return ds

 

3.3.3.3 模型搭建与训练

本实验采用的是全连接神经网络算法,所以我们首先需要建立初始化的神经网络。nn.cell能够用来组成网络模型;模型包括5个卷积层和RELU激活函数,一个全连接输出层并使用softmax进行多分类,共分成(0-9)10类。利用定义类的方式生成网络,Mindspore中定义网络需要继承nn.cell。在init方法中定义该网络需要的神经网络层,在construct方法中梳理神经网络层与层之间的关系。

class ForwardNN(nn.Cell):
    def __init__(self):
        super(ForwardNN, self).__init__()
        self.flatten = nn.Flatten()
        self.relu = nn.ReLU()
        self.fc1 = nn.Dense(784, 512, activation='relu')
        self.fc2 = nn.Dense(512, 256, activation='relu')
        self.fc3 = nn.Dense(256, 128, activation='relu')
        self.fc4 = nn.Dense(128, 64, activation='relu')
        self.fc5 = nn.Dense(64, 32, activation='relu')
        self.fc6 = nn.Dense(32, 10, activation='softmax')

    def construct(self, input_x):
        output = self.flatten(input_x)
        output = self.fc1(output)
        output = self.fc2(output)
        output = self.fc3(output)
        output = self.fc4(output)
        output = self.fc5(output)
        output = self.fc6(output)
        return output

指定模型所需的损失函数、评估指标、优化器等参数,然后将创建好的网络、损失函数、评估指标、优化器等参数装入模型中对模型进行训练。

lr = 0.001
num_epoch = 8
momentum = 0.9

net = ForwardNN()
# 定义loss函数,改函数不需要求导,可以给离散的标签值,且loss值为均值
loss = nn.loss.SoftmaxCrossEntropyWithLogits(sparse=True, reduction='mean')
# 定义准确率为评价指标,用于评价模型
metrics = {"Accuracy": Accuracy(), "Confusion_matrix": nn.ConfusionMatrix(num_classes= 10)}
# 定义优化器为Adam优化器,并设定学习率
opt = nn.Adam(net.trainable_params(), lr)
# 生成验证集,验证机不需要训练,所以不需要repeat
ds_eval = create_dataset(False, batch_size=32)
# 模型编译过程,将定义好的网络、loss函数、评价指标、优化器编译
model = Model(net, loss, opt, metrics)
# 生成训练集
ds_train = create_dataset(True, batch_size=32)
print("============== 开始训练 ==============")
# 训练模型,用loss作为监控指标,并利用昇腾芯片的数据下沉特性进行训练
model.train(num_epoch, ds_train, callbacks=[LossMonitor()], dataset_sink_mode=True)
# 使用测试集评估模型,打印总体准确率
metrics_result = model.eval(ds_eval)
res = metrics_result["Confusion_matrix"]

 

3.3.4 基于pytorch的前馈神经网络

搭建的网络结构的输入层有784个节点;三个隐藏层,每层20个节点;输出层有10个节点。
class BP:
    def __init__(self):
        self.input = np.zeros((100, 784))   # 100 samples per round
        self.hidden_layer_1 = np.zeros((100, 20))
        self.hidden_layer_2 = np.zeros((100, 20))
        self.hidden_layer_3 = np.zeros((100, 20))
        self.output_layer = np.zeros((100, 10))
        self.w1 = 2 * np.random.random((784, 20)) - 1   # limit to (-1, 1)
        self.w2 = 2 * np.random.random((20, 20)) - 1
        self.w3 = 2 * np.random.random((20, 20)) - 1
        self.w4 = 2 * np.random.random((20, 10)) - 1
        self.error = np.zeros(10)
        self.learning_rate = 0.1

    def sigmoid(self, x):
        return 1 / (1 + np.exp(-x))

    def sigmoid_deri(self, x):
        return x * (1 - x)

    def forward_prop(self, data, label):   # label:100 X 10,data: 100 X 784
        self.input = data
        self.hidden_layer_1 = self.sigmoid(np.dot(self.input, self.w1))
        self.hidden_layer_2 = self.sigmoid(np.dot(self.hidden_layer_1, self.w2))
        self.hidden_layer_3 = self.sigmoid(np.dot(self.hidden_layer_2, self.w3))
        self.output_layer = self.sigmoid(np.dot(self.hidden_layer_3, self.w4))
        self.error = label - self.output_layer
        return self.output_layer

    def backward_prop(self):
        output_diff = self.error * self.sigmoid_deri(self.output_layer)
        hidden_diff_3 = np.dot(output_diff, self.w4.T) * self.sigmoid_deri(self.hidden_layer_3)
        hidden_diff_2 = np.dot(hidden_diff_3, self.w3.T) * self.sigmoid_deri(self.hidden_layer_2)
        hidden_diff_1 = np.dot(hidden_diff_2, self.w2.T) * self.sigmoid_deri(self.hidden_layer_1)
        # update
        self.w4 += self.learning_rate * np.dot(self.hidden_layer_3.T, output_diff)
        self.w3 += self.learning_rate * np.dot(self.hidden_layer_2.T, hidden_diff_3)
        self.w2 += self.learning_rate * np.dot(self.hidden_layer_1.T, hidden_diff_2)
        self.w1 += self.learning_rate * np.dot(self.input.T, hidden_diff_1)

 

4 实验结果及分析

4.1 实验结果

4.1.1 基于MindSporeSoftmax回归

4.1.1.1 准确率

c6d52199276442ec87680706ce29e25e.png

可见经过20轮的训练,测试集准确率收敛于0.92左右。训练集和测试集准确率随训练轮数的变化曲线如下:

 5f612dc6e3884a1a82496970fcf2ac3a.png

 

4.1.1.2 混淆矩阵

 

1d0e2e7055a143808218a7f75ba01bfb.png

 

4.1.2 基于pytorchSoftmax回归

4.1.2.1 准确率

 172157d38f5a46c88260f00cc92c0fc2.png  

 88c715f661b741a59fb8f0831d65dd87.png

18ac842c0df74476a51da9d588cd478b.png 

可见经过20轮的训练,测试集准确率收敛于0.92左右,这与MindSpore的性能较为相似。

 

4.1.2.2 混淆矩阵

6ba25ad0bcd442079f6f16242728afdb.png

 1804f5e3117f4514a491e1d14c75b11a.png

 

4.1.3 基于MindSpore的前馈神经网络

4.1.3.1 准确率

013c7eae82614ab5bb8cb5577aa57da0.png

基于MindSpore的前馈神经网络经过较少的轮数即可收敛,正确率为0.88左右。

 

4.1.3.2 混淆矩阵

9ae3e2850fb644a49926c4159897d8b4.png 

4.1.4 基于pytorch的前馈神经网络

4.1.4.1 准确率

d73968df43584ffe8c433f2f49bde02a.png

基于pytorch的前馈神经网络同样经过较少轮数即可收敛,准确率为92.6%左右,高于基于MindSpore的前馈神经网络。

 

4.1.4.2 混淆矩阵

64850b8aa1944ecd8bbb840190557997.png

 

4.2 结果分析与对比

下表列出了各模型的准确率对比情况:

模型

准确率

基于MindSpore的Softmax回归

92%

基于pytorch的Softmax回归

92%

基于MindSpore的前馈神经网络

88%

基于pytorch的前馈神经网络

92.6%

可见除了基于MindSpore的前馈神经网络,其余模型的准确率都在92%左右。通过实验发现,Softmax回归需要经过20轮左右才能收敛,而前馈神经网络在10轮之内即可收敛,所以前馈神经网络在运行时间上性能更优。此外若仔细观察各模型的混淆矩阵可以发现,将正确标签为9的误判为4、将正确标签为5的误判为3、正确标签为2的误判为8这几种误分类情况较为普遍,这是这些数字的手写体较为相近,容易混淆的缘故。

 

 

MSbp网络手写识别.py

# 导入相关依赖库
import os
import numpy as np
from matplotlib import pyplot as plt
import mindspore as ms
# context模块用于设置实验环境和实验设备
import mindspore.context as context
# dataset模块用于处理数据形成数据集
import mindspore.dataset as ds
# c_transforms模块用于转换数据类型
import mindspore.dataset.transforms as C
# vision.c_transforms模块用于转换图像,这是一个基于opencv的高级API
import mindspore.dataset.vision as CV
# 导入Accuracy作为评价指标
from mindspore.nn.metrics import Accuracy
# nn中有各种神经网络层如:Dense,ReLu
from mindspore import nn
# Model用于创建模型对象,完成网络搭建和编译,并用于训练和评估
from mindspore.train import Model
# LossMonitor可以在训练过程中返回LOSS值作为监控指标
from mindspore.train.callback import LossMonitor

# 设定运行模式为动态图模式,并且运行设备为昇腾芯片
context.set_context(mode=context.GRAPH_MODE, device_target='CPU')
# MindSpore内置方法读取MNIST数据集
ds_train = ds.MnistDataset(os.path.join(r'D:\Dataset\MNIST', "train"))
ds_test = ds.MnistDataset(os.path.join(r'D:\Dataset\MNIST', "test"))

print('训练数据集数量:', ds_train.get_dataset_size())
print('测试数据集数量:', ds_test.get_dataset_size())
# 该数据集可以通过create_dict_iterator()转换为迭代器形式,然后通过get_next()一个个输出样本
image = ds_train.create_dict_iterator().get_next()

print('图像长/宽/通道数:', image['image'].shape)
# 一共10类,用0-9的数字表达类别。
print('一张图像的标签样式:', image['label'])
DATA_DIR_TRAIN = "D:/Dataset/MNIST/train"  # 训练集信息
DATA_DIR_TEST = "D:/Dataset/MNIST/test"  # 测试集信息


def create_dataset(training=True, batch_size=128, resize=(28, 28), rescale=1 / 255, shift=-0.5, buffer_size=64):
    ds = ms.dataset.MnistDataset(DATA_DIR_TRAIN if training else DATA_DIR_TEST)

    # 定义改变形状、归一化和更改图片维度的操作。
    # 改为(28,28)的形状
    resize_op = CV.Resize(resize)
    # rescale方法可以对数据集进行归一化和标准化操作,这里就是将像素值归一到0和1之间,shift参数可以让值域偏移至-0.5和0.5之间
    rescale_op = CV.Rescale(rescale, shift)
    # 由高度、宽度、深度改为深度、高度、宽度
    hwc2chw_op = CV.HWC2CHW()

    # 利用map操作对原数据集进行调整
    ds = ds.map(input_columns="image", operations=[resize_op, rescale_op, hwc2chw_op])
    ds = ds.map(input_columns="label", operations=C.TypeCast(ms.int32))
    # 设定洗牌缓冲区的大小,从一定程度上控制打乱操作的混乱程度
    ds = ds.shuffle(buffer_size=buffer_size)
    # 设定数据集的batch_size大小,并丢弃剩余的样本
    ds = ds.batch(batch_size, drop_remainder=True)
    return ds

# 显示前10张图片以及对应标签,检查图片是否是正确的数据集
dataset_show = create_dataset(training=False)
data = dataset_show.create_dict_iterator().get_next()
images = data['image'].asnumpy()
labels = data['label'].asnumpy()

for i in range(1, 11):
    plt.subplot(2, 5, i)
    # 利用squeeze方法去掉多余的一个维度
    plt.imshow(np.squeeze(images[i]))
    plt.title('Number: %s' % labels[i])
    plt.xticks([])
plt.show()

# 利用定义类的方式生成网络,Mindspore中定义网络需要继承nn.cell。在init方法中定义该网络需要的神经网络层
# 在construct方法中梳理神经网络层与层之间的关系。
class ForwardNN(nn.Cell):
    def __init__(self):
        super(ForwardNN, self).__init__()
        self.flatten = nn.Flatten()
        self.relu = nn.ReLU()
        self.fc1 = nn.Dense(784, 512, activation='relu')
        self.fc2 = nn.Dense(512, 256, activation='relu')
        self.fc3 = nn.Dense(256, 128, activation='relu')
        self.fc4 = nn.Dense(128, 64, activation='relu')
        self.fc5 = nn.Dense(64, 32, activation='relu')
        self.fc6 = nn.Dense(32, 10, activation='softmax')

    def construct(self, input_x):
        output = self.flatten(input_x)
        output = self.fc1(output)
        output = self.fc2(output)
        output = self.fc3(output)
        output = self.fc4(output)
        output = self.fc5(output)
        output = self.fc6(output)
        return output

lr = 0.001
num_epoch = 8
momentum = 0.9

net = ForwardNN()
# 定义loss函数,改函数不需要求导,可以给离散的标签值,且loss值为均值
loss = nn.loss.SoftmaxCrossEntropyWithLogits(sparse=True, reduction='mean')
# 定义准确率为评价指标,用于评价模型
metrics = {"Accuracy": Accuracy(), "Confusion_matrix": nn.ConfusionMatrix(num_classes= 10)}
# 定义优化器为Adam优化器,并设定学习率
opt = nn.Adam(net.trainable_params(), lr)
# 生成验证集,验证机不需要训练,所以不需要repeat
ds_eval = create_dataset(False, batch_size=32)
# 模型编译过程,将定义好的网络、loss函数、评价指标、优化器编译
model = Model(net, loss, opt, metrics)
# 生成训练集
ds_train = create_dataset(True, batch_size=32)
#print("============== 开始训练 ==============")
# 训练模型,用loss作为监控指标,并利用昇腾芯片的数据下沉特性进行训练
model.train(num_epoch, ds_train, callbacks=[LossMonitor()], dataset_sink_mode=True)
# 使用测试集评估模型,打印总体准确率
metrics_result = model.eval(ds_eval)
res = metrics_result["Confusion_matrix"]

# 绘制混淆矩阵
classes = ['0', '1', '2', '3', '4', '5','6','7','8','9']
def plot_confusion_matrix(cm, title='Confusion Matrix'):
    plt.figure(figsize=(12, 8), dpi=100)
    np.set_printoptions(precision=2)
    # 混淆矩阵中每格的值
    ind_array = np.arange(len(classes))
    x, y = np.meshgrid(ind_array, ind_array)
    for x_val, y_val in zip(x.flatten(), y.flatten()):
        c = cm[y_val][x_val]
        if c > 0.001:
            plt.text(x_val, y_val, "%0.2f" % (c,), color='#EE3B3B', fontsize=10, va='center', ha='center')
    plt.imshow(cm, interpolation='nearest', cmap=plt.cm.binary)
    plt.title(title)
    plt.colorbar()
    xlocations = np.array(range(len(classes)))
    plt.xticks(xlocations, classes, rotation=90)
    plt.yticks(xlocations, classes)
    plt.ylabel('Actual Label')
    plt.xlabel('Predict Label')
    tick_marks = np.array(range(len(classes))) + 0.5
    plt.gca().set_xticks(tick_marks, minor=True)
    plt.gca().set_yticks(tick_marks, minor=True)
    plt.gca().xaxis.set_ticks_position('none')
    plt.gca().yaxis.set_ticks_position('none')
    plt.grid(True, which='minor', linestyle='-')
    plt.gcf().subplots_adjust(bottom=0.15)
    plt.show()

plot_confusion_matrix(res, title='Confusion Matrix')
print(metrics_result)

MSsoftmax手写识别.py

import mindspore
import struct
from sklearn.metrics import confusion_matrix
from mindspore.common.initializer import Normal
import mindspore.dataset.vision as cv
from IPython import display
import os
import numpy as np
from matplotlib import pyplot as plt
import mindspore.dataset as ds
import mindspore.dataset.transforms as C
from mindspore import nn

# 导入已下载的数据集
def load_mnist(path, kind='train'):
    # os.path.join()函数用于路径拼接文件路径
    labels_path = os.path.join(path, '%s-labels.idx1-ubyte' % kind)
    images_path = os.path.join(path, '%s-images.idx3-ubyte' % kind)

    # 读取训练集标签数据集
    with open(labels_path, 'rb') as lbpath:
        magic, n = struct.unpack('>II',lbpath.read(8))
        # 使用struct.unpack方法读取前两个数据。lbpath.read(8)表示一次从文件中读取8个字节
        # 这样读到的前两个数据分别是magic number(2049)和样本个数(60000)
        labels = np.fromfile(lbpath,dtype=np.uint8)
        # 读取标签,标签的数值在0~9之间

    # 读取训练集图片数据集
    with open(images_path, 'rb') as imgpath:
        magic, num, rows, cols = struct.unpack('>IIII',imgpath.read(16))
        # 使用struct.unpack方法读取前四个数据。lbpath.read(16)表示一次从文件中读取16个字节
        # 这样读到的前四个数据分别是magic number(2051)、图像数量(60000)、图像的高rows(28)、图像的宽columns(28)
        images = np.fromfile(imgpath,dtype=np.uint8).reshape(len(labels), 28, 28, 1)  # 设置图像形状,高度宽度均为28,通道数为1
    return images, labels
    # labels形状为(60000,)
    # images形状为(60000, 28, 28, 1)

# 创建一个迭代器类,作为GeneratorDataset的数据源
class FashionMnist():
    def __init__(self, path, kind):  # 定义数据初始化等操作,在实例化数据集对象时被调用
        self.data, self.label = load_mnist(path, kind)

    def __getitem__(self, index):  # 定义该函数后可使其支持随机访问,能够根据给定的索引值index,获取数据集中的数据并返回
        return self.data[index], self.label[index]

    def __len__(self):  # 返回数据集的样本数量
        return len(self.data)
    # 在完成自定义数据集类之后,可以通过GeneratorDataset接口按照用户定义的方式加载并访问数据集样本

# 将Fashion-MNIST数据集加载到内存中
def load_data_fashion_mnist(data_path, batch_size, resize=None, works=1):

    mnist_train = FashionMnist(data_path, kind='train')  # 读取训练集
    mnist_test = FashionMnist(data_path, kind='t10k')    # 读取测试集
    mnist_train = ds.GeneratorDataset(source=mnist_train, column_names=['image', 'label'],
                                      shuffle=False, python_multiprocessing=False)
    mnist_test = ds.GeneratorDataset(source=mnist_test, column_names=['image', 'label'],
                                     shuffle=False, python_multiprocessing=False)
    # 数据变换
    trans = [cv.Rescale(1.0 / 255.0, 0), cv.HWC2CHW()] # 数据做标准化处理,所得到的数值分布满足正态分布
    # 调整图像的像素大小。Rescale变换用于调整图像像素值的大小,包括两个参数:
    # rescale:缩放因子。shift:平移因子。图像的每个像素将根据这两个参数
    # 进行调整,输出的像素值为 outputi = inputi ∗ rescale+shift
    # HWC2CWH变换用于转换图像格式,(height, width, channel)转为(channel, height, width)
    type_cast_op = C.TypeCast(mindspore.int32)  # 将输入的Tensor转换为指定的数据类型
    if resize:
        trans.insert(0, cv.Resize(resize))  # 调整为给定的尺寸大小
    mnist_train = mnist_train.map(trans, input_columns=["image"])
    mnist_test = mnist_test.map(trans, input_columns=["image"])
    mnist_train = mnist_train.map(type_cast_op, input_columns=['label'])
    mnist_test = mnist_test.map(type_cast_op, input_columns=['label'])

    mnist_train = mnist_train.batch(batch_size, num_parallel_workers=works)
    mnist_test = mnist_test.batch(batch_size, num_parallel_workers=works)

    return mnist_train, mnist_test

batch_size = 256
mnist_train, mnist_test = load_data_fashion_mnist('D:/Dataset/MNIST' ,batch_size)

# nn.SequentialCell是一个有序的Cell容器。输入Tensor将按照定义的顺序通过所有Cell。
# 我们可以使用SequentialCell来快速组合构造一个神经网络模型
net = nn.SequentialCell([nn.Flatten(), nn.Dense(784, 10, weight_init=Normal(0.01, 0), bias_init='zero')])
# nn.Flatten将输入的X维度从[256,1,28,28]变成[256,784],则一个样本数据一行

# 损失函数SoftmaxCrossEntropyWithLogits,交叉熵损失函数中传递未规范化的预测,并同时计算softmax及其损失
loss = nn.SoftmaxCrossEntropyWithLogits(sparse=True, reduction='mean')
# 优化器SGD,学习率为0.1的随机梯度下降
optim = nn.SGD(net.trainable_params(), learning_rate=0.1)

# 累加器
class Accumulator:

    def __init__(self, n):
        self.data = [0.0] * n

    def add(self, *args):
        self.data = [a + float(b) for a, b in zip(self.data, args)]

    def reset(self):
        self.data = [0.0] * len(self.data)

    def __getitem__(self, idx):
        return self.data[idx]

# 计算预测正确的数量
def accuracy(y_hat, y):
    if len(y_hat.shape) > 1 and y_hat.shape[1] > 1:   # 判断y_hat是不是矩阵
        y_hat = y_hat.argmax(axis=1)                  # 得到每样本预测概率最大所属分类的下标
    cmp = y_hat.asnumpy() == y.asnumpy()              # y_hat.asnumpy() == y.asnumpy()返回的是一个布尔数组
    return float(cmp.sum())

# 绘制混淆矩阵
classes = ['0', '1', '2', '3', '4', '5','6','7','8','9']
def plot_confusion_matrix(cm, title='Confusion Matrix'):
    plt.figure(figsize=(12, 8), dpi=100)
    np.set_printoptions(precision=2)
    # 混淆矩阵中每格的值
    ind_array = np.arange(len(classes))
    x, y = np.meshgrid(ind_array, ind_array)
    for x_val, y_val in zip(x.flatten(), y.flatten()):
        c = cm[y_val][x_val]
        if c > 0.001:
            plt.text(x_val, y_val, "%0.2f" % (c,), color='#EE3B3B', fontsize=10, va='center', ha='center')
    plt.imshow(cm, interpolation='nearest', cmap=plt.cm.binary)
    plt.title(title)
    plt.colorbar()
    xlocations = np.array(range(len(classes)))
    plt.xticks(xlocations, classes, rotation=90)
    plt.yticks(xlocations, classes)
    plt.ylabel('Actual Label')
    plt.xlabel('Predict Label')
    tick_marks = np.array(range(len(classes))) + 0.5
    plt.gca().set_xticks(tick_marks, minor=True)
    plt.gca().set_yticks(tick_marks, minor=True)
    plt.gca().xaxis.set_ticks_position('none')
    plt.gca().yaxis.set_ticks_position('none')
    plt.grid(True, which='minor', linestyle='-')
    plt.gcf().subplots_adjust(bottom=0.15)
    plt.show()

# 计算在指定数据集上模型的精度;得到混淆矩阵
def evaluate_accuracy(net, data_iter):
    metric = Accumulator(2)         # 累加器,metric[0]记录正确预测数,metric[1]记录预测总数
    hunxiao=np.zeros((10,10))
    for X, y in data_iter:
        metric.add(accuracy(net(X), y), y.size)
        y_hat = net(X).argmax(axis=1)
        hunxiao+=confusion_matrix(y.asnumpy(),y_hat.asnumpy())
    plot_confusion_matrix(hunxiao,  title='Confusion Matrix')
    return metric[0] / metric[1]    # 正确预测数 / 预测总数

# 训练模型一个迭代周期
def train_epoch(net, train_iter, loss, optim):
    net_with_loss = nn.WithLossCell(net, loss)                # 将net与loss连接
    net_train = nn.TrainOneStepCell(net_with_loss, optim)     # 将net,loss,optim连接,生成训练模型
    metric = Accumulator(3)
    for X, y in train_iter:
        l = net_train(X, y)
        y_hat = net(X)
        metric.add(float(l.sum().asnumpy()),accuracy(y_hat, y), y.size)
    return  metric[0] / metric[2], metric[1] / metric[2]

# 训练模型
def trainer(net, train_iter, test_iter, loss, num_epochs, optim):
    global train_metrics, test_acc
    animator = Animator(xlabel='epoch', xlim=[1, num_epochs], ylim=[0.3, 0.9],
                        legend=['train accuracy', 'test accuracy'])
    for epoch in range(num_epochs):
        train_metrics = train_epoch(net, train_iter, loss, optim)
        aaa,train_accuracy=train_metrics
        train_accuracy=round(train_accuracy,4)
        test_acc = evaluate_accuracy(net, test_iter)
        print("第",epoch+1,"轮训练集正确率为",train_accuracy,";测试集正确率为",test_acc)
        animator.add(epoch + 1, train_metrics + (test_acc,))
    train_acc = train_metrics

def set_axes(axes, xlabel, ylabel, xlim, ylim, xscale, yscale, legend):
    axes.set_xlabel(xlabel)
    axes.set_ylabel(ylabel)
    axes.set_xscale(xscale)
    axes.set_yscale(yscale)
    axes.set_xlim(xlim)
    axes.set_ylim(ylim)
    if legend:
        axes.legend(legend)
    axes.grid()

class Animator:
    def __init__(self, xlabel=None, ylabel=None, legend=None, xlim=None,
                 ylim=None, xscale='linear', yscale='linear',
                 fmts=('-', 'm--', 'g-.', 'r:'), nrows=1, ncols=1,
                 figsize=(3.5, 2.5)):
        if legend is None:
            legend = []
        display.display_svg()
        self.fig, self.axes =plt.subplots(nrows, ncols, figsize=figsize)
        if nrows * ncols == 1:
            self.axes = [self.axes, ]
        self.config_axes = lambda: set_axes(
            self.axes[0], xlabel, ylabel, xlim, ylim, xscale, yscale, legend)
        self.X, self.Y, self.fmts = None, None, fmts

    def add(self, x, y):
        if not hasattr(y, "__len__"):
            y = [y]
        n = len(y)
        if not hasattr(x, "__len__"):
            x = [x] * n
        if not self.X:
            self.X = [[] for _ in range(n)]
        if not self.Y:
            self.Y = [[] for _ in range(n)]
        for i, (a, b) in enumerate(zip(x, y)):
            if a is not None and b is not None:
                self.X[i].append(a)
                self.Y[i].append(b)
        self.axes[0].cla()
        for x, y, fmt in zip(self.X, self.Y, self.fmts):
            self.axes[0].plot(x, y, fmt)
        self.config_axes()
        display.display(self.fig)
        display.clear_output(wait=True)

num_epochs = 20
trainer(net, mnist_train, mnist_test, loss, num_epochs, optim)
plt.show()

pytorchBP网络手写识别.py

# coding=gbk
import numpy as np
import os
from matplotlib import pyplot as plt
from sklearn.metrics import confusion_matrix
os.environ["KMP_DUPLICATE_LIB_OK"]="TRUE"
import torchvision
import torchvision.transforms as transforms
# 输入层:784个节点;隐藏层:三个隐藏层,每层20个节点
# 输出层:10个节点
class BP:
    def __init__(self):
        self.input = np.zeros((100, 784))   # 100 samples per round
        self.hidden_layer_1 = np.zeros((100, 20))
        self.hidden_layer_2 = np.zeros((100, 20))
        self.hidden_layer_3 = np.zeros((100, 20))
        self.output_layer = np.zeros((100, 10))
        self.w1 = 2 * np.random.random((784, 20)) - 1   # limit to (-1, 1)
        self.w2 = 2 * np.random.random((20, 20)) - 1
        self.w3 = 2 * np.random.random((20, 20)) - 1
        self.w4 = 2 * np.random.random((20, 10)) - 1
        self.error = np.zeros(10)
        self.learning_rate = 0.1

    def sigmoid(self, x):
        return 1 / (1 + np.exp(-x))

    def sigmoid_deri(self, x):
        return x * (1 - x)

    def forward_prop(self, data, label):   # label:100 X 10,data: 100 X 784
        self.input = data
        self.hidden_layer_1 = self.sigmoid(np.dot(self.input, self.w1))
        self.hidden_layer_2 = self.sigmoid(np.dot(self.hidden_layer_1, self.w2))
        self.hidden_layer_3 = self.sigmoid(np.dot(self.hidden_layer_2, self.w3))
        self.output_layer = self.sigmoid(np.dot(self.hidden_layer_3, self.w4))
        self.error = label - self.output_layer
        return self.output_layer

    def backward_prop(self):
        output_diff = self.error * self.sigmoid_deri(self.output_layer)
        hidden_diff_3 = np.dot(output_diff, self.w4.T) * self.sigmoid_deri(self.hidden_layer_3)
        hidden_diff_2 = np.dot(hidden_diff_3, self.w3.T) * self.sigmoid_deri(self.hidden_layer_2)
        hidden_diff_1 = np.dot(hidden_diff_2, self.w2.T) * self.sigmoid_deri(self.hidden_layer_1)
        # update
        self.w4 += self.learning_rate * np.dot(self.hidden_layer_3.T, output_diff)
        self.w3 += self.learning_rate * np.dot(self.hidden_layer_2.T, hidden_diff_3)
        self.w2 += self.learning_rate * np.dot(self.hidden_layer_1.T, hidden_diff_2)
        self.w1 += self.learning_rate * np.dot(self.input.T, hidden_diff_1)

def load_data():
    # 第一次运行时download=True
    datasets_train = torchvision.datasets.MNIST(root='D:/Dataset/pytorch/', train=True, transform=transforms.ToTensor(), download=True)
    datasets_test = torchvision.datasets.MNIST(root='D:/Dataset/pytorch/', train=False, transform=transforms.ToTensor(), download=True)
    data_train = datasets_train.data
    X_train = data_train.numpy()
    X_test = datasets_test.data.numpy()
    X_train = np.reshape(X_train, (60000, 784))
    X_test = np.reshape(X_test, (10000, 784))
    Y_train = datasets_train.targets.numpy()
    Y_test = datasets_test.targets.numpy()
    real_train_y = np.zeros((60000, 10))
    real_test_y = np.zeros((10000, 10))
    # each y has ten dimensions
    for i in range(60000):
        real_train_y[i, Y_train[i]] = 1
    for i in range(10000):
        real_test_y[i, Y_test[i]] = 1
    index = np.arange(60000)
    np.random.shuffle(index)

    X_train = X_train[index]
    real_train_y = real_train_y[index]
    X_train = np.int64(X_train > 0)
    X_test = np.int64(X_test > 0)
    return X_train, real_train_y, X_test, real_test_y

def bp_network():
    nn = BP()
    X_train, Y_train, X_test, Y_test = load_data()
    batch_size = 100
    epochs = 6000
    for epoch in range(epochs):
        start = (epoch % 600) * batch_size
        end = start + batch_size
        print(start, end)
        nn.forward_prop(X_train[start: end], Y_train[start: end])
        nn.backward_prop()
    return nn

# 绘制混淆矩阵
classes = ['0', '1', '2', '3', '4', '5','6','7','8','9']
def plot_confusion_matrix(cm, title='Confusion Matrix'):
    plt.figure(figsize=(12, 8), dpi=100)
    np.set_printoptions(precision=2)
    # 混淆矩阵中每格的值
    ind_array = np.arange(len(classes))
    x, y = np.meshgrid(ind_array, ind_array)
    for x_val, y_val in zip(x.flatten(), y.flatten()):
        c = cm[y_val][x_val]
        if c > 0.001:
            plt.text(x_val, y_val, "%0.2f" % (c,), color='#EE3B3B', fontsize=10, va='center', ha='center')
    plt.imshow(cm, interpolation='nearest', cmap=plt.cm.binary)
    plt.title(title)
    plt.colorbar()
    xlocations = np.array(range(len(classes)))
    plt.xticks(xlocations, classes, rotation=90)
    plt.yticks(xlocations, classes)
    plt.ylabel('Actual Label')
    plt.xlabel('Predict Label')
    tick_marks = np.array(range(len(classes))) + 0.5
    plt.gca().set_xticks(tick_marks, minor=True)
    plt.gca().set_yticks(tick_marks, minor=True)
    plt.gca().xaxis.set_ticks_position('none')
    plt.gca().yaxis.set_ticks_position('none')
    plt.grid(True, which='minor', linestyle='-')
    plt.gcf().subplots_adjust(bottom=0.15)
    plt.show()

def bp_test():
    nn = bp_network()
    sum = 0
    X_train, Y_train, X_test, Y_test = load_data()
    y=np.array(Y_test)
    y=np.argmax(y,axis=1)
    y_pre=[]
    for i in range(len(X_test)):
        res = nn.forward_prop(X_test[i], Y_test[i])
        res = res.tolist()
        index = res.index(max(res))
        y_pre.append(index)
        if Y_test[i, index] == 1:
            sum += 1
    print(confusion_matrix(y, y_pre))
    print('预测准确率:', sum / len(Y_test))
    plot_confusion_matrix(confusion_matrix(y, y_pre), title='Confusion Matrix')

if __name__ == '__main__':
    bp_test()

pytorchSoftmax手写识别.py

import numpy as np
import torch
from matplotlib import pyplot as plt
from sklearn.metrics import confusion_matrix
from torch import nn,optim
from torchvision import datasets,transforms
from torch.utils.data import DataLoader

# 训练集
train_data = datasets.MNIST(root="D:/Dataset/pytorch/",train = True, transform=transforms.ToTensor(), download = True )
# 测试集
test_data = datasets.MNIST(root="D:/Dataset/pytorch/",train = False,transform=transforms.ToTensor(),download = True)
# 批次大小
batch_size = 64
# 装载训练集
train_loader = DataLoader(dataset=train_data,batch_size=batch_size,shuffle=True)
# 装载测试集
test_loader = DataLoader(dataset=test_data,batch_size=batch_size,shuffle=True)

# 定义网络结构
class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()  # 初始化
        self.fc1 = nn.Linear(784, 10)  # 784个输入10个输出
        self.softmax = nn.Softmax(dim=1)  # 激活函数 dim=1表示对第一个维度进行概率计算

    def forward(self, x):
        # torch.Size([64, 1, 28, 28]) -> (64,784)
        x = x.view(x.size()[0], -1)  # 4维变2维 (在全连接层做计算只能2维)
        x = self.fc1(x)  # 传给全连接层继续计算
        x = self.softmax(x)  # 使用softmax激活函数进行计算
        return x

# 定义模型
model = Net()
# 定义代价函数
mse_loss = nn.MSELoss()
# 定义优化器
optimizer = optim.SGD(model.parameters(),lr=0.5)
# 定义模型训练和测试的方法
def train():
    for i, data in enumerate(train_loader):
        # 获得一个批次的数据和标签
        inputs, labels = data
        # 获得模型预测结果(64,10)
        out = model(inputs)
        # to onehot 把数据标签变成独热编码
        labels = labels.reshape(-1, 1)  # 先把1维变成2维(64)-(64,1)
        # tensor.scatter(dim,index,src)
        # dim:对那个维度进行独热编码
        # index:要将src中对应的值放到tensor那个位置
        # src:插入index的数值
        one_hot = torch.zeros(inputs.shape[0], 10).scatter(1, labels, 1)
        # 计算loss   mse_loss的两个数据的shape要一致
        loss = mse_loss(out, one_hot)
        # 梯度清零
        optimizer.zero_grad()
        # 计算梯度
        loss.backward()
        # 修改权值
        optimizer.step()

# 绘制混淆矩阵
classes = ['0', '1', '2', '3', '4', '5','6','7','8','9']
def plot_confusion_matrix(cm, title='Confusion Matrix'):
    plt.figure(figsize=(12, 8), dpi=100)
    np.set_printoptions(precision=2)
    # 混淆矩阵中每格的值
    ind_array = np.arange(len(classes))
    x, y = np.meshgrid(ind_array, ind_array)
    for x_val, y_val in zip(x.flatten(), y.flatten()):
        c = cm[y_val][x_val]
        if c > 0.001:
            plt.text(x_val, y_val, "%0.2f" % (c,), color='#EE3B3B', fontsize=10, va='center', ha='center')
    plt.imshow(cm, interpolation='nearest', cmap=plt.cm.binary)
    plt.title(title)
    plt.colorbar()
    xlocations = np.array(range(len(classes)))
    plt.xticks(xlocations, classes, rotation=90)
    plt.yticks(xlocations, classes)
    plt.ylabel('Actual Label')
    plt.xlabel('Predict Label')
    tick_marks = np.array(range(len(classes))) + 0.5
    plt.gca().set_xticks(tick_marks, minor=True)
    plt.gca().set_yticks(tick_marks, minor=True)
    plt.gca().xaxis.set_ticks_position('none')
    plt.gca().yaxis.set_ticks_position('none')
    plt.grid(True, which='minor', linestyle='-')
    plt.gcf().subplots_adjust(bottom=0.15)
    plt.show()

def test():
    correct = 0
    y=[]
    y_pre=[]
    for i, data in enumerate(test_loader):
        # 获得一个批次的数据和标签
        inputs, labels = data
        # 获得模型预测结果(64,10)
        out = model(inputs)
        # 获得最大值,以及最大值所在的位置
        _, predicted = torch.max(out, 1)
        # 预测正确的数量
        correct += (predicted == labels).sum()
        y_pre.append(np.array(predicted).flatten().tolist())
        y.append(np.array(labels).flatten().tolist())
    y_pre = [n for a in y_pre for n in a]
    y = [n for a in y for n in a]
    print(confusion_matrix(y, y_pre))
    plot_confusion_matrix(confusion_matrix(y, y_pre), title='Confusion Matrix')
    print("Test acc:{0}".format(correct.item() / len(test_data)))

# 训练
for epoch in range(20):
    print("epoch:",epoch)
    train()
    test()

 

参考资料

https://www.cnblogs.com/Luv-GEM/p/10694471.html

python实现混淆矩阵

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/445823.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

UBUNTU下NFS配置(用于嵌入式开发)

1. NFS简介 NFS(Network File System)即网络文件系统,是FreeBSD支持的文件系统中的一种,它允许网络中的计算机之间共享资源。在NFS的应用中,本地NFS的客户端应用可以透明地读写位于远端NFS服务器上的文件,就…

低代码(九)低代码平台后设计一:模型驱动

我们先看一下汽车的基本构造,由车身、发动机、方向盘等多个零部件构成,因为它是一个工业产品,有实物存在,摸得着看得见,所以大家很容易理解。日本丰田汽车是如何做到自动化流水线生产的,本质上是把产品xBOM…

BufferedOutputStream,BufferedInputStream是字节流,对象处理流,序列化,输入输出流,转换流

BufferedInputStream字节输入流 意思就是InputStream类及其子类都能以参数的形式放到BufferedInputStream构造器的参数 package com.hspedu.outputstream_;import java.io.*;/*** author 韩顺平* version 1.0* 演示使用BufferedOutputStream 和 BufferedInputStream使用* 使用他…

数据挖掘:心脏病预测(测评指标;EDA)

目录 一、前期准备 二、实战演练 2.1分类指标评价计算示例 2.2数据探索性分析(EDA) 2.2.1 导入函数工具箱 2.2.2 查看数据信息等相关数据 判断数据缺失和异常 数字特征相互之间的关系可视化 类别特征分析(箱图,小提琴图&am…

ios客户端学习笔记(五):学习Swift的关键字和容易弄混的符号

1. 关键字 下面是Swift语言中的常见关键字及其说明和代码应用实例: class:定义一个类,用于封装一组相关的属性和方法。 示例代码: class Person {var name: String ""var age: Int 0 }struct:定义一个…

网络安全与攻防-常见网络安全攻防

目录 攻击手段&防御策略 阻断服务攻击(DoS) 地址解析欺骗(ARP攻击)(Address Resolution Protocol spoofing) 跨站脚本攻击(XSS) SQL注入 跨站请求伪造(csrf&am…

AlgoC++第二课:线性回归

目录 线性回归前言1. 定义2. 房价预测案例2.1 定义问题:2.2 MSE是如何被定义的?(如何推导的?)2.3 总结 3. 代码3.1 C实现3.1.1 数据读取和处理3.1.2 线性回归模型3.1.3 参数输出和预测3.1.4 完整示例代码3.1.5 C知识点 3.2 python实现3.2.1 房…

【移动端网页布局】移动端网页布局基础概念 ① ( 移动端浏览器 | 移动端屏幕分辨率 | 移动端网页调试方法 )

文章目录 一、移动端浏览器二、移动端屏幕分辨率三、移动端网页调试方法 一、移动端浏览器 移动端浏览器 比 PC 端浏览器发展要晚 , 使用的技术比较新 , 对 HTML5 CSS3 支持较好 , 常见的浏览器如下 : UC / QQ / Opera / Chrom / 360 / 百度 / 搜狗 / 猎豹 国内的浏览器 基本…

算法套路十一 ——回溯法之组合型回溯

算法套路十一 ——回溯法之组合型回溯 该节是在上一节回溯法之子集型回溯的基础上进行描写,组合型回溯会在子集型回溯的基础上判断所选子集是否符合组合要求, 故请首先阅读上一节算法套路十——回溯法之子集型回溯 算法示例:LeetCode77. 组合…

【C++ 十八】C++ map/ multimap容器

C map/ multimap 容器 文章目录 C map/ multimap 容器前言1 map 基本概念2 map 构造和赋值3 map 大小和交换4 map 插入和删除5 map 查找和统计6 map 容器排序 总结 前言 本文包含map基本概念、map构造和赋值、map大小和交换、map插入和删除、map查找和统计、map容器排序。 1 m…

使用Glib中测试框架对C代码进行单元测试

C项目的测试框架比较常见的是Google的gtest(前文CMake项目使用ctestgtest进行单元测试有使用实例介绍gtest,感兴趣的读者可以去看看),也有一些其它框架,比如Boost中的测试框架。这些框架虽然也可以测试C代码&#xff0…

Vue 消息订阅与发布

消息订阅与发布,也可以实现任意组件之间的通信。 订阅者:就相当于是我们,用于接收数据。 发布者:就相当于是媒体,用于传递数据。 安装消息订阅与发布插件: 在原生 JS 中 不太容易实现消息订阅与发布&…

Unity-ML-Agents-代码解读-RollerBall

使用版本:https://github.com/Unity-Technologies/ml-agents/releases/tag/release_19 文件路径:ml-agents-release_19/docs/Learning-Environment-Create-New.md 20和19的在rollerBall上一样:https://github.com/Unity-Technologies/ml-ag…

CSDN博客编写教程

这里写自定义目录标题 欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants 创建一个自定义列表如何创建一个…

osg widget 试用 方法

按钮 一个常见的 osg::Widget 就是按钮。下面的代码展示了如何使用 osg::Switch 和 osgText 创建一个简单的按钮&#xff1a; osg::ref_ptr<osg::Switch> buttonSwitch new osg::Switch(); osg::ref_ptr<osgText::Text> buttonText new osgText::Text(); buttonT…

浏览器不好用?插件来帮忙

一、目的 浏览器本身具备的功能并不完善&#xff0c;不同的用户可以为自己浏览器增加想要功能&#xff0c;使得浏览器更能符合自己的需求&#xff0c;提高浏览器使用的舒适度 二、推荐插件 AdblockPlus LastPass&#xff08;密码记录&#xff0c;全平台通用&#xff09; Dar…

JSON的用法和说明

JSON&#xff08;JavaScript Object Notation&#xff09;是一种轻量级的数据交换格式。 JSON建构于两种结构&#xff1a; "名称/值"对的集合。理解为对象 值的有序列表。理解为数组 JSON具有以下这些形式&#xff1a; 对象是一个无序的“ ’名称/值‘ 对”集合。一个…

阿里 Arthas (阿尔萨斯)工具的使用

目录 使用 一、安装与启动命令行控制台使用 使用 这款工具可以监控线上、测试或者其他环境的java运行中程序的情况&#xff0c;用于定位线上、测试等环境的问题。 一、安装与启动 通过termius远程登录测试或者线上环境&#xff0c;cd到指定目录下&#xff0c;输入命令&#…

高效部署Redis Sentinel模式(哨兵模式),手把手教学

Redis Sentinel模式部署 前言一、服务器部署同版本的redis1、换软件源在yum拉取包的时候启用remi源 二、修改配置文件1.修改/etc/redis.conf2.配置/etc/redis/sentinel.conf 三、启动redis服务1、启动服务2、连接redis3、检查redis 前言 这里就不过多的解释高可用的好处了&…

设计模式:行为型模式 - 迭代器模式

文章目录 1.概述2.结构3.案例实现4.优缺点5.使用场景6.JDK源码解析 1.概述 定义&#xff1a; 提供一个对象来顺序访问聚合对象中的一系列数据&#xff0c;而不暴露聚合对象的内部表示。 2.结构 迭代器模式主要包含以下角色&#xff1a; 抽象聚合&#xff08;Aggregate&…