概要
我们训练了一个基于MLP的分类器,用于区分手写数字图像
介绍
多层感知器(Multilayer Perceptron,简称MLP)是一种前馈神经网络(Feedforward Neural Network)模型,由多个神经元组成。每一层通过非线性变换将输入映射到下一层,最终输出一个非线性的预测结果。MLP可以应用于分类、回归和其他机器学习任务。
整体架构流程
1、导入需要用到的库函数
import numpy as np
from torchvision.datasets import MNIST
from scipy.special import expit
import matplotlib.pyplot as plt
import cv2
2、加载MNIST数据集
#load MNIST dataset
data_path='./data'
train_data=MNIST(data_path,train=True,download=True)
test_data=MNIST(data_path,train=False,download=True)
3、对数据集进行处理
train_list=list(train_data)
test_list=list(test_data)
4、建立多层感知器模型
# Define the Multi-Layer Perceptron (MLP) class
class MLP():
'''Multi layer Perceptron'''
def __init__(self,num_input_node,num_hidden_node,num_hidden_node1,num_output_node,lr=0.1):
self.num_inpot_node=num_input_node
self.num_hidden_node=num_hidden_node
self.num_hidden_node1=num_hidden_node1
self.num_output_node=num_output_node
self.lr=lr
self.time=list()
self.loss=list()
self.t=1
#Initial weight between
self.wih=np.random.normal(0,pow(self.num_hidden_node,-0.5),(self.num_hidden_node,self.num_inpot_node))
self.whh=np.random.normal(0,pow(self.num_hidden_node1,-0.5),(self.num_hidden_node1,self.num_hidden_node))
self.who=np.random.normal(0,pow(self.num_output_node,-0.5),(self.num_output_node,self.num_hidden_node1))
self.activation_fun=lambda x:expit(x)
def predict(self,input):
output_hidden=self.activation_fun(np.dot(self.wih,input))
output_hidden1=self.activation_fun(np.dot(self.whh,output_hidden))
result=self.activation_fun(np.dot(self.who,output_hidden1))
result=result.argmax()
return result
def train(self,input,label,num_iter=3):
for i in range(num_iter):
# step 1:predict
out_hidden=self.activation_fun(np.dot(self.wih,input))
out_hidden1=self.activation_fun(np.dot(self.whh,out_hidden))
out_output=self.activation_fun(np.dot(self.who,out_hidden1))
# setp 2:Comput error
self.time.append(self.t)
self.t=self.t+1
self.loss.append(np.mean(np.abs(out_output - label)))
error_output=out_output-label
error_hidden1=np.dot(self.who.T,error_output)
error_hidden=np.dot(self.whh.T,error_hidden1)
#step 3:Update weight
self.wih+=-self.lr*np.dot(error_hidden*out_hidden*(1-out_hidden),input.T)
self.whh+=-self.lr*np.dot(error_hidden1*out_hidden1*(1-out_hidden1),out_hidden.T)
self.who+=-self.lr*np.dot(error_output*out_output*(1-out_output),out_hidden1.T)
predict_result=out_output.argmax()
groundtruth=label.argmax()
if i%2==0:
print('predict:{},label:{} Correct:{}'.format(predict_result,groundtruth,predict_result==groundtruth))
def plot(self):
plt.plot(self.time,self.loss,color='blue', linestyle='-')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend()
plt.title('Loss Function Curve')
plt.show()
5、设置参数
# Set parameters
num_input_node=28*28
num_hidden_node=80
num_hidden_node1=60
num_output_node=10
lr=0.001
6、训练
#train
cnt_correct=0
accuracy=list()
for i,data in enumerate(train_list):
input=data[0]
input=np.array(input)
input=input.reshape(num_input_node,1)
input=input/255.0
label=np.zeros((num_output_node,1))
label[int(data[1])]=1
mlp.train(input,label)
print("Data{}has been trained".format(i))
if i%100==0:
for i ,data in enumerate(test_list):
input = data[0]
input=np.array(input)
input = input.reshape(num_input_node,1)
input=input/255.0
result = mlp.predict(input)
if result==data[1]:
cnt_correct+=1
accuracy.append(cnt_correct*100.0/len(test_list))
cnt_correct=0
mlp.plot()
训练的损失如下
7、训练过程中测试集的准确率
#Draw test set accuracy
plt.plot(range(1,len(accuracy)+1),accuracy,color='red', linestyle='-')
plt.xlabel('Epoch')
plt.ylabel('accuracy')
plt.legend()
plt.title('Accuracy Curve')
plt.show()
结果如下
8、测试
#test
cnt_correct=0
for i ,data in enumerate(test_list):
input = data[0]
input=np.array(input)
input = input.reshape(num_input_node,1)
input=input/255.0
result = mlp.predict(input)
print("predict:{}:laberl:{}".format(result,data[1]))
if result==data[1]:
cnt_correct+=1;
print("Test accuracy:{}%".format(cnt_correct*100.0/len(test_list)))
9、加载自己的手写数字并验证
import cv2
import matplotlib.pyplot as plt
input=np.array(cv2.imread("./image/2.jpg"))
input=input[:,:,0]
for i in range (28):
for j in range(28):
if(input[i][j]>120):
input[i][j]=255
else:
input[i][j]=0
plt.imshow(input)
input = input.reshape(num_input_node,1)
result = mlp.predict(input)
print("The predicted result is {}".format(result))
完整代码
import numpy as np
from torchvision.datasets import MNIST
from scipy.special import expit
import cv2
#load MNIST dataset
data_path='./data'
train_data=MNIST(data_path,train=True,download=True)
test_data=MNIST(data_path,train=False,download=True)
train_list=list(train_data)
# train_list = train_list[:2000]
test_list=list(test_data)
class MLP():
'''Multi layer Perceptron'''
def __init__(self,num_input_node,num_hidden_node,num_hidden_node1,num_output_node,lr=0.1):
'''
Initial
:param num_input_node: Number of input nodes
:param num_input_node: Number of output nodes
'''
self.num_inpot_node=num_input_node
self.num_hidden_node=num_hidden_node
self.num_hidden_node1=num_hidden_node1
self.num_output_node=num_output_node
self.lr=lr
#Initial weight between
self.wih=np.random.normal(0,pow(self.num_hidden_node,-0.5),(self.num_hidden_node,self.num_inpot_node))
self.whh=np.random.normal(0,pow(self.num_hidden_node1,-0.5),(self.num_hidden_node1,self.num_hidden_node))
self.who=np.random.normal(0,pow(self.num_output_node,-0.5),(self.num_output_node,self.num_hidden_node1))
# self.activation_fun=lambda x:expit(x)
self.activation_fun=lambda x:expit(x)
def predict(self,input):
output_hidden=self.activation_fun(np.dot(self.wih,input))
output_hidden1=self.activation_fun(np.dot(self.whh,output_hidden))
result=self.activation_fun(np.dot(self.who,output_hidden1))
result=result.argmax()
# print("input:{} predict:{}".format(input[1],result))
return result
def train(self,input,label,num_iter=4):
for i in range(num_iter):
# step 1:predict
out_hidden=self.activation_fun(np.dot(self.wih,input)) #80,1
out_hidden1=self.activation_fun(np.dot(self.whh,out_hidden)) #80,1
out_output=self.activation_fun(np.dot(self.who,out_hidden1)) #10,1
# setp 2:Comput error
error_output=out_output-label #10,1
error_hidden1=np.dot(self.who.T,error_output) #80,1
error_hidden=np.dot(self.whh.T,error_hidden1) #80,1
#step 3:Update weight
self.wih+=-self.lr*np.dot(error_hidden*out_hidden*(1-out_hidden),input.T)
self.whh+=-self.lr*np.dot(error_hidden1*out_hidden1*(1-out_hidden1),out_hidden.T)
self.who+=-self.lr*np.dot(error_output*out_output*(1-out_output),out_hidden1.T)
predict_result=out_output.argmax()
groundtruth=label.argmax()
if i%2==0:
print('predict:{},label:{} Correct:{}'.format(predict_result,groundtruth,predict_result==groundtruth))
# print(self.lr*np.dot(error_hidden*out_hidden*(1-out_hidden),input.T))
num_input_node=28*28
num_hidden_node=150
num_hidden_node1=120
num_output_node=10
lr=0.001
mlp=MLP(num_input_node,num_hidden_node,num_hidden_node1,num_output_node,lr)
#train
for i,data in enumerate(train_list):
input=data[0]
input=np.array(input)
input=input.reshape(num_input_node,1)
label=np.zeros((num_output_node,1))
label[int(data[1])]=1
mlp.train(input,label)
print("Data{}has been trained".format(i))
#test
cnt_correct=0
for i ,data in enumerate(test_list):
input = data[0]
input=np.array(input)
input = input.reshape(num_input_node,1)
result = mlp.predict(input)
print("result{}:pre{}".format(result,data[1]))
if result==data[1]:
cnt_correct+=1;
print("Test accuracy:{}%".format(cnt_correct*100.0/len(test_list)))
小结
我们首先对MNIST数据集进行了加载和预处理,将像素值转换为0到1之间的浮点数,并将数据集分为60000个训练集和10000个测试集。接着,我们构建了一个基于MLP的分类器,它包含了三个隐藏层和一个输出层。使用Sigmoid激活函数,而输出层包含了10个神经元,对应于0到9的10个数字类别。 MLP使用梯度下降法法训练,在每个批次上对权重进行更新,以最小化损失函数,同时根据每次训练完后的损失值画出损失函数图像。我们对MLP模型进行了超参数调整,包括隐藏层数和神经元个数,同时也对数据进行了归一化处理。我们发现,当我们增加隐藏层数时,模型能够更好地区分数字,但是也需要更多的时间进行训练。我们还发现,增加每个隐藏层中的神经元数可以提高模型的精度,但也需要更长的训练时间。最后,在测试集上对模型进行评估,我们发现该模型的准确率可以达到90.8%。结合实验结果,我们可以得出结论:MLP是一种灵活且有效的分类器,可以用于各种分类问题中