首页 > 其他分享 >MNIST-BP神经网络 & AdaBoost——框架解决

MNIST-BP神经网络 & AdaBoost——框架解决

时间:2022-12-07 19:23:12浏览次数:60  
标签:plt nn np BP AdaBoost MNIST import data mnist

MNIST

THE MNIST DATABASE of handwritten digits

BP 神经网络

模型训练

import os
import numpy as np
import torch
import torch.utils.data # 数据读取包
import matplotlib.pyplot as plt
from time import time
from torchvision import datasets, transforms
from torch import nn, optim

cd=os.path.dirname(__file__)    # 定义当前工作目录为代码所在目录

# 定义图片的处理方式
transf=transforms.Compose([transforms.ToTensor(),transforms.Normalize((0.5,), (0.5,)),])

# 下载并加载训练数据集
train_set = datasets.MNIST(cd+'/train_set', # 下载至代码文件夹中
download=not os.path.exists(cd+'/train_set'),   # 不重复下载
train=True, # 定义为训练集
transform=transf    # 图片的处理
)

# 构建数据集的DataLoader对象
train_loader = torch.utils.data.DataLoader(train_set, batch_size=64, shuffle=True)  # 定义训练集,64张图片一组,打乱次序

dataiter = iter(train_loader)   # 获取一个训练集的迭代器

# print(images.shape) # torch.Size([64, 1, 28, 28]) 64张图片一组,每张图片一个颜色通道,尺寸为28x28
# print(labels.shape) # torch.Size([64])

# plt.imshow(images[0].numpy().squeeze(), cmap='gray_r')
# 以灰度图格式展示第一张图片,使用squeeze()函数将images[0]转换为二维矩阵(去掉多余的维度)

class NeuralNetwork(nn.Module):
    def __init__(self):
        super().__init__()
        # 使用nn.Sequential创建前向传播的序列
        self.model=nn.Sequential(nn.Linear(28*28, 128), # 输入层有128个神经元,接受所有像素的输入
        nn.ReLU(),  # 使用ReLU函数做激活函数
        nn.Linear(128,64),  # 隐藏层有64个神经元
        nn.ReLU(),  
        nn.Linear(64,10),   # 输出层有10个神经元
        nn.LogSoftmax(dim=1)
        )
    
    def forward(self,x):
        # 定义前向传播,x:图片数据(硬性规定)
        # x-shape为(64, 1, 28, 28),将其转化为(64,784)
        x=x.view(x.shape[0], -1)

        #前向传播
        x=self.model(x)
        return x

model=NeuralNetwork()
# 使用负对数似然损失函数定义神经网络的损失函数
C=nn.NLLLoss()
# 定义优化器,使用随机梯度下降法,学习率手动设置,momentum默认为0.9(用于防止过拟合)
r=float(input("请输入学习率:"))
Optimizer=optim.SGD(model.parameters(), lr=r, momentum=0.9)


time0=time()    # 记录当前时间
epochs=15   # 训练15轮
for i in range(93):
    images, labels = next(dataiter) # 迭代训练集元素
    for e in range(epochs):
        running_loss=0  # 本轮损失
        for images, labels in train_loader:
            # 前向传播
            output = model(images)
            # 计算损失
            loss = C(output,labels)
        #    print(output)
        #    print(labels)
        #    k=input()
            # 反向传播
            loss.backward()

            # 更新权重
            Optimizer.step()

            # 清空梯度
            Optimizer.zero_grad()

            # 累加损失
            running_loss += loss.item()
        else:
            # 一轮结束后打印本轮的损失函数
            print("Set {} with Epoch {} - Training loss: {}".format(i+1,e+1, running_loss/len(train_loader)))

# 打印总训练时间
print("\nTraining Time (in minutes) =",(time()-time0)/60)
torch.save(model, cd+"/BPM") # 存储模型文件至当前文件夹下的"BPM"文件

模型测试

import os
import numpy as np
import torch
import torchvision
import torch.utils.data # 数据读取包
import matplotlib.pyplot as plt
from time import time
from torchvision import datasets, transforms
from torch import nn, optim
from sklearn import metrics # sklearn的包名是scikit-learn,装sklearn是没用的

# 定义神经网络
class NeuralNetwork(nn.Module):
    def __init__(self):
        super().__init__()
        # 使用nn.Sequential创建前向传播的序列
        self.model=nn.Sequential(nn.Linear(28*28, 128), # 输入层有128个神经元,接受所有像素的输入
        nn.ReLU(),  # 使用ReLU函数做激活函数
        nn.Linear(128,64),  # 隐藏层有64个神经元
        nn.ReLU(),  
        nn.Linear(64,10),   # 输出层有10个神经元
        nn.LogSoftmax(dim=1)
        )   
    def forward(self,x):
        # 定义前向传播,x:图片数据(硬性规定)
        # x-shape为(64, 1, 28, 28),将其转化为(64,784)
        x=x.view(x.shape[0], -1)
        #前向传播
        x=self.model(x)
        return x

# 定义图片的处理方式
transf=transforms.Compose([transforms.ToTensor(),transforms.Normalize((0.5,), (0.5,)),])

# 下载并加载测试数据集
test_set = datasets.MNIST('test_set',   # 下载
download=not os.path.exists('test_set'),    # 不重复
train=False,    # 测试集
transform=transf    # 图片处理
)

print("Information for test_set.")
print(test_set)

# 构建数据集的DataLoader对象
test_loader = torch.utils.data.DataLoader(test_set, shuffle=True)    # 定义测试集
checkiter = iter(test_loader)   # 获取一个测试集的迭代器

model=torch.load(os.path.dirname(__file__)+"/BPM")  # 载入已经训练好的模型

torch.no_grad() # 测试无梯度

# F1指标分析
data=[] # 输出
lab=[] # 真实值
pred=[]
for img_, lab_ in test_loader:
    tmp=model(img_)
    data.append(tmp.detach().numpy())
    lab.append(lab_.detach().numpy())
    _, pre=torch.max(tmp.data, 1)
    pred.append(pre.detach().numpy()[0])
data=np.array(data)
d_shape=data.shape
data=data.reshape(d_shape[0],d_shape[2])
lab=np.array(lab).reshape(d_shape[0])
pred=np.array(pred)
rep=metrics.classification_report(lab,pred)
print(rep)

# ROC曲线
sizeS=d_shape[0]
for i in range(10):
    score_=[]
    for j in range(sizeS):
        score_.append(data[j][i])
    fpr, tpr, thresholds=metrics.roc_curve(lab, np.array(score_), pos_label=i)
    auc=metrics.auc(fpr, tpr)
    plt.plot(fpr, tpr,  lw=1, label="Number "+str(i)+", Auc={:.3f}".format(auc))
plt.plot([0, 1], [0, 1], color="navy", lw=1, linestyle="--")
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.05])
plt.xlabel("False Positive Rate")
plt.ylabel("True Positive Rate")
plt.title("ROC for each type of number")
plt.legend()
plt.show()

F1-Score的

手动分析

TP_=np.zeros(10)   # 真阳性,预测数字为n且实际数字也为n的个数
FP_=np.zeros(10)   # 假阳性,预测数字为n但实际数字不为n的个数(存在FPn中)
FN_=np.zeros(10)   # 假阴性,预测数字不为n但实际数字为n的个数
TN_=np.zeros(10)   # 真阴性,预测数字不为n且实际数字也不为n的个数

# 记录以目标数字为真实值的测试样例标签以及对应的输出值,用于绘制ROC
Roc_Label=[[],[],[],[],[],[],[],[],[],[]]   # 标签
Roc_Output=[[],[],[],[],[],[],[],[],[],[]]  # 输出值

for (imgs, labels) in test_loader:
    pred=model(imgs)
    shape=pred.shape
    for j in range(shape[0]):
        cnt+=1  # 统计总的测试次数
        ans=labels[j]   # 实际数字
        flag=pred[j][0] 
        res=0   # 预测数字
        for k in range(shape[1]):
            if(pred[j][k]>flag):    # 搜索概率最大的
                flag=pred[j][k]
                res=k
        if(res!=ans):   # 将一次测试拆成十份分别判断各类别的四性质,但总的比例不改变
            for ii in range(10):
                if(ii==res):
                    FP_[res]+=1  # 预测数字的假阳
                elif(ii==ans):
                    FN_[ans]+=1  # 实际数字的假阴
                else:   # 其他数字的真阴
                    TN_[ii]+=1
        else:
            for ii in range(10):
                if(ii!=res):    # 真阴
                    TN_[ii]+=1
                else:   #真阳
                    TP_[ii]+=1
        Roc_Label[ans].append(res)
        Roc_Output[ans].append(pred[j][ans].item())    # 真实值的对应概率,pred提取出dim=0的tensor类型的变量,用item()将其转化为数值
        
# 四性质
TP=TP_.sum()    # 真阳
FP=FP_.sum()    # 假阳
FN=FN_.sum()    # 假阴
TN=TN_.sum()    # 真阴    

# 三指标+F1-Score
pec=100 # 百分
acu=((TP+TN)/(TP+TN+FP+FN)) # 准确率
pre=TP/(TP+FP)  # 精确率
rec=TP/(TP+FN)  # 召回率
F1=(2*pre*rec)/(pre+rec)    # F1

# ROC处理
tpr=[[]]*10 # 获取10*1的列表
fpr=[[]]*10
auc=[[]]*10
for i in range(10):     # 按十个类别分别绘制ROC曲线并叠加
    labels=np.array(Roc_Label[i])   # 取出标签
    op=np.array(Roc_Output[i])  # 取出输出值
    fpr_tmp, tpr_tmp, theresholds = metrics.roc_curve(labels, op, pos_label=i)
    tpr[i]=tpr_tmp  # 真阳率
    fpr[i]=fpr_tmp  # 真阴率
    auc[i]=metrics.auc(fpr_tmp, tpr_tmp)    # 计算auc

F1-Score可以综合类别统计,也可以做二元统计。这里采取了宏平均,即各类别评分权值,也可根据支持率做宏加权处理。

同时ROC做了错误处理,只取了各类别只有TP而没有TN的情况,这会使得绘制的ROC曲线的FPR(即横坐标)的数量急剧减少

参考:机器学习中的F1-Score

train-images.idx3-ubyte 文件格式

00 00 08 03 03表示数据维度为3维(60000x28x28)

00 00 EA 60 EA60 H为60000 D,表明有6w组图片数据

00 00 00 1C x2 表示图片尺寸为28x28=784 Bytes,每个像素值在0-255之间,用一个字节表示
(784*60000+16=47040016 D=2CDC610 H,恰好对应文件的字节数)

随后就是第一组图片数据。

输出层设计-Softmax

使用神经网络进行分类任务时,分类的类别数目即为输出层的神经元个数。输出函数使用softmax函数:约束各个输出点的值在(0,1)之间且和为1。输出结果表示该类别的概率,取概率最大的结点所代表的类别为预测类别

对于手写数字识别,输出层将有10个神经元,分别代表数字0-9,如某个案例的标签为9,则训练目标就是使得0-8号神经元的输出尽可能接近0,而9号神经元的输出尽可能接近1。

softmax函数会放大数值间的差距,但也存在溢出的风险,因此计算指数时可以先将输出值减去最大值,来规避溢出的风险。

参考:[

深度学习常用激活函数,

Softmax解析,

]

Pytorch

指标

F1指标:https://zhuanlan.zhihu.com/p/62180318

ROC曲线必须按类别单独绘制,它只服务于二分类器。(顺便学学matplotlib.pyplot的可视化)。使用sklearn提取TPR与FPR时需要的两组数据为预测序列与(真实输出数字的概率,而非预测结果的概率)输出值,将其视为二值分类器,则序列的范围要取在全体序列上。最开始我取了只有TP而没有TN的情况,也就是只取了真实值为该类别的序列。

参考:[

ROC1

ROC2

]

Numpy小技巧

  • tp_num=(labels==1).sum() :统计labels中1出现的次数。labels==1 返回一个逻辑数组,求和即可。

  • output_sort=np.flipud(np.argsort(outPut))	# argsort获取从小到大排序的索引数组,flipud将其逆序,即变为从大到小的索引序列。
    
    labels=labels[output_sort]  # 利用数组索引按输出值从大到小同时排序标签与输出值
    outPut=outPut[output_sort]
    

    参考:numpy排序

AdaBoost

matplotlib-RC自定义配置

iris数据集

imshow-cmap参数

sklearn 模型的保存与读取

sklearn.metrics详解(指标分析)

模型训练

import numpy as np
import os

from sklearn.datasets import fetch_openml,load_iris
from sklearn.ensemble import AdaBoostClassifier
import joblib   # joblib在高版本的sklearn中已经被移除

import time

# 获取mnist数据集
mnist=fetch_openml('mnist_784', version=1, cache=True)  #默认需要已安装pandas包
mnist.target=mnist.target.astype(np.int8)   # 标签类型转换为int8
mnist_train_data=np.array(mnist.data[:60000]) # 从pandas数据集转化为数组
mnist_train_target=np.array(mnist.target[:60000])   # 前6万个设置为训练数据集

mnist_test_data=np.array(mnist.data[60000:])    # 后1万个为测试数据集
mnist_test_target=np.array(mnist.target[60000:])

# Adaboost
# 定义分类器
model=AdaBoostClassifier(
    base_estimator=None,    # 基分类器的种类默认为决策树模型
    n_estimators=500,    # 基分类器个数
    learning_rate=0.1,  # 学习率
    algorithm='SAMME.R',    # boosting算法默认选用SAMME.R,对样本按错分率划分
    random_state=None   # 随机数种子
)

time0=time.time()   
# 训练模型
model=model.fit(mnist_train_data,mnist_train_target)  # 输入为数据与标签的ndarray,返回仍是同类分类器

# 计时模型训练时间
time1=time.time()   
timed=(time1-time0)/60
print("It costs time:"+"%0.2f"%timed+" Minutes") # 类c++的printf写法

# 保存模型
cd=os.path.dirname(__file__)
modelName="AdaB"
joblib.dump(model, cd+"/"+modelName)

print("模型准确率:{:2.1f}".format(model.score(mnist_test_data,mnist_test_target)*100))

模型测试

import os

import numpy as np

import matplotlib as mpl
import matplotlib.pyplot as plt

from sklearn.datasets import fetch_openml
from sklearn.ensemble import AdaBoostClassifier
import sklearn.metrics as mtr

import joblib

cd=os.path.dirname(__file__)+"/"
# 读取模型
modelName="AdaB"
model=joblib.load(cd+modelName)

# 读取数据
print("Reading datas...")
mnist=fetch_openml(
    "mnist_784",
    version=1,
    cache=True
)
mnist_data=np.array(mnist.data)
mnist_tar=np.array(mnist.target).astype(np.int8)   # 一定要转换类型,默认为字符,影响预测
test_data=mnist_data[60000:]
test_tar=mnist_tar[60000:]

# 图表预设
# https://my.oschina.net/swuly302/blog/94805 matplotlib RC自定义配置,美化plot图表
mpl.rc("axes", labelsize=14)    # x轴y轴字体大小
mpl.rc("xtick", labelsize=12)   # x轴刻度标签字体大小
mpl.rc("ytick", labelsize=12)   
mpl.rcParams['font.sans-serif']=[u'SimHei'] # 衬线
mpl.rcParams['axes.unicode_minus']=False    # 减号使用连字符号

# 展示图片
def plot_digit(data):
    image=data.reshape(28, 28)
    plt.imshow(image, cmap=mpl.cm.binary, # 黑底白字
                interpolation="nearest") # 处理图片
    plt.axis("off") # 无轴线
    plt.show()  # 展示图片
# some_digit=mnist_train_data[36000]
# plot_digit(some_digit)

# 查看已经训练的模型信息
def someInformation(mdl):
    print(len(mdl.estimators_)) # 查看分类器的个数
    print(mdl.estimator_weights_)   # 查看分类器权重
    print(mdl.estimator_errors_) # 查看分类器错分率
    print(mdl.feature_importances_) # 特征重要性

print("Predicting...")
# 模型评估
pec=100
acu=model.score(test_data,test_tar)
print("模型准确率:{:.2f}%".format(acu*pec))

# F1
pred=model.predict(test_data)
rep=mtr.classification_report(test_tar, pred)   # 获取数据分析报告
print(rep)

# 按类别绘制ROC曲线
score=model.predict_proba(test_data)
sizeS=10000
for i in range(10):
    score_=[]
    for j in range(sizeS):
        score_.append(score[j][i])  # 提取目标输出
    fpr_, tpr_, thresholds=mtr.roc_curve(pred, np.array(score_), pos_label=i)
    auc=mtr.auc(fpr_, tpr_)
    plt.plot(fpr_, tpr_, lw=1, label="Number "+str(i)+", Auc={:.3f}".format(auc))
plt.plot([0, 1], [0, 1], color="navy", lw=1, linestyle="--")
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.05])
plt.xlabel("False Positive Rate")
plt.ylabel("True Positive Rate")
plt.title("ROC for each type of number")
plt.legend()
plt.show()

sklearn

所读取的Mnist集中的标签数据 .target 默认为字符串类型,需要将其转换为数值类型后再用作模型预测,否则会出现预测准确率始终为0的情况。

转化类型:

mnist.target=mnist.target.astype(np.int8)

参考:[

sklearn参数详解

sklearn处理Mnist集

]

Python编码

编码总是让人头疼的问题,各种解码编码存储格式真分不清。

u'abc' 将字符串abc用unicode的形式存储,unicode是"统一码

",它包括了ASCII码,一般使用UTF-8(16,32)进行编码。

不同的编码方式对字符串的处理准则不同,最后编码出的结果就不同。解码 encode 会将字符串转换为unicode对象,编码 decode 会将unicode对象转换为普通字符串。

在使用 u'abc' 时,python会预先用utf-8编码识别该字符并将其转换成unicode对象,相当于提前做了编码标记,哪怕代码文件的编码格式发生了变化,解析该对象时所用的编码仍是固定的。

神奇的Python语法

  • k=sorted([(a, c) for a, c in enumerate(b)])
    # -> [(0, [1, 2]), (1, [3, 4]), (2, [5, 6])]
    k1=np.array(k)
    # -> array([[0, list([1, 2])],
    #       [1, list([3, 4])],
    #       [2, list([5, 6])]], dtype=object)
    
  • time.time() 获取程序运行至当前位置所花费的时间

标签:plt,nn,np,BP,AdaBoost,MNIST,import,data,mnist
From: https://www.cnblogs.com/Forest-set-you/p/16954118.html

相关文章

  • Webpack完整打包流程分析
    前言webpack在前端工程领域起到了中流砥柱的作用,理解它的内部实现机制会对你的工程建设提供很大的帮助(不论是定制功能还是优化打包)。下面我们基于webpack5源码结构,对......
  • Webpack插件核心原理
    引言围绕Webpack打包流程中最核心的机制就是所谓的Plugin机制。所谓插件即是webpack生态中最关键的部分,它为社区用户提供了一种强有力的方式来直接触及webpack......
  • Webpack中的高级特性
    自从webpack4以后,官方帮我们集成了很多特性,比如在生产模式下代码压缩自动开启等,这篇文章我们一起来探讨一下webpack给我们提供的高级特性助力开发。探索webpack的高级特性......
  • view-design tabpane禁用后renderHeader失效问题
    需求是这样的在tabPane的renderHeader里面添加hover事件(使用组件自带的Poptip)能显示提示其实这个不算是问题,设置disabled属性后,原本的元素上面添加了 ivu-tabs-tab-disa......
  • JS逆向之webpack 通用扣取思路
    本文所有教程及源码、软件仅为技术研究。不涉及计算机信息系统功能的删除、修改、增加、干扰,更不会影响计算机信息系统的正常运行。不得将代码用于非法用途,如侵立删!标题......
  • Abp自动注册的坑
    在abp开发中遇到一个大坑就是service的名称必须与接口名”I“后面的名称完全一致,否则不会注入如:publicclassAccountService:ApplicationService,IAccountsService......
  • Webpage工具
    Webpage工具谷歌开源测试工具,可以模拟从世界各地的服务器去访问网站进行测试,并输出测试结果。可用于客户需要仅仅访问单个网站或者域名加速时,使用该工具进行解析,可生成re......
  • dotnet new cli 以及Abp-cli命令的简单使用
    首先dotnetnew .NETCore3.1SDK及更高版本dotnetnew -根据指定的模板,创建新的项目、配置文件或解决方案从.NET7SDK开始, dotnetnew 语法已更改:--list、-......
  • MobPush for Uni-app
    插件集成访问​​https://ext.dcloud.net.cn/plugin?id=10280​​点击购买并添加到项目当中。在uniapp的“manifest.json”中选择“app原生插件配置”,点击勾选mob-push插件......
  • KBPC1010-ASEMI液压升降装置方案整流桥10A 1000V
    编辑-ZKBPC1010在KBPC-4封装里采用的4个芯片,其尺寸都是100MIL,是一款10A1000V液压升降装置方案整流桥。KBPC1010的浪涌电流Ifsm为200A,漏电流(Ir)为10uA,其工作时耐温度范围为......