这篇文章讲使用pytorch和CPU的。
所以若没有安装 pytorch, 需要安装 pytorch。
背景:
(看正文可以直接跳过到“引入库”)
写这篇博客的原因:
本文主要目的通过我的第一个神经网络二分类器的创建过程,来帮助我自己了解搭建一个简单神经网络的过程,也希望能够帮助到寻找搭建简单的神经网络而遇到困难的人。
下面是一个简单的神经网络二分类器:
若有不足,欢迎建议。
一、引入库:
import numpy
import torch
import datetime
import math
设置输入,以21个输入为例,使用CPU :
shuru=21
device = torch.device('cpu') # 使用CPU
二、准备工作
定义一个MoXing类:
MoXing: 模型
定义你想要的神经网络模型
class MoXing(torch.nn.Module):
def __init__(self):
super(MoXing, self).__init__()
self.hid1 = torch.nn.Linear(shuru,59)#定义第一个隐藏层(可无)
#第一个参数输入(一般为上一层神经元数),这里shuru就是刚才定义的输入数
#第一个参数输出(一般为下一层神经元数)
self.hid2 = torch.nn.Linear(59, 59)#第二层(可无),同理
self.oupt = torch.nn.Linear(59, 1)
torch.nn.init.xavier_uniform_(self.hid1.weight)#第一层w
torch.nn.init.zeros_(self.hid1.bias)#第一层b
torch.nn.init.xavier_uniform_(self.hid2.weight)
torch.nn.init.zeros_(self.hid2.bias)
torch.nn.init.xavier_uniform_(self.oupt.weight)
torch.nn.init.zeros_(self.oupt.bias)
def forward(self, x):
z = torch.tanh(self.hid1(x))#第一层激活函数tanh
z = torch.tanh(self.hid2(z))#同理
z = torch.sigmoid(self.oupt(z)) #输出层激活函数sigmoid
return z
定义一个ShuJuJi类:
ShuJuJi:数据集
方便一会处理测试数据和训练数据,本文主要用csv表格中的数据。
数据集第一列为要预测的y,其他列为输入的特征,可根据自己需要改。
class ShuJuJi(torch.utils.data.Dataset):
def __init__(self, src_file):
all_data = numpy.loadtxt(src_file, usecols=range(0,shuru+1),delimiter=",", comments="#", dtype="float",skiprows=1)
self.y_data = torch.tensor(all_data[:,0],dtype=torch.float32).to(device)
#第一列为y
self.y_data = self.y_data.reshape(-1,1)
self.x_data = torch.tensor(all_data[:,1:shuru+1],dtype=torch.float32).to(device)
#之后第二列到第shuru+1列shuru个列为x
def __len__(self):
return len(self.x_data)
def __getitem__(self, hang):
x = self.x_data[hang,:] # 第hang+1行的x
y = self.y_data[hang,:] # 第hang+1行的y
return x, y # 返回 x, y
定义ping_gu函数:
ping_gu: 评估
用来评估神经网络效果
#预测值大于分界,将预测值视为1类,否则为0类
def ping_gu(moxing, shujuji, fenjie=0.5):
a = 0; b = 0; c = 0; d = 0
for i in range(len(shujuji)):
x = shujuji[i][0]
y = shujuji[i][1]
y = y.type(torch.int64) #y 化为整数
with torch.no_grad():
y_yuce = moxing(x)
if y == 1 and y_yuce >= fenjie:
a += 1
elif y == 0 and y_yuce < fenjie:
b += 1
elif y == 0 and y_yuce >= fenjie:
c += 1
elif y == 1 and y_yuce < fenjie:
d += 1
N = a + c + b + d
if N = len(shujuji) and N != 0:
zb1 = (a + b) / (N * 1.0)
#指标1: 预测的准确率
if a+c != 0:
zb2= (1.0 * a) / (a + c)
#指标2: 预测为1类中真实值为1的概率
else:
zb2=0.0
if a+d != 0:
zb3 = (1.0 * a) / (a + d)
#指标3: 真实值为1中指标2预测为1类的概率
else:
zb3=0.0
if zb3 != 0 and zb2!= 0:
zb_four = 2.0 / ((1.0 / zb2) + (1.0 / zb3))
#指标_four: 指标2和指标3的调和平均数
else:
zb_four = 0
return zb1, zb2, zb3, zb_four
else:
return 0, 0, 0, 0
三、开始 :
(为了运行后的显示效果,下面print中部分文字可能与代码不太对应,不要在意。)
启动!
print("\t\t\t神经网络:\t\t (名字)")#(名字): 神经网路名字,自己取
print('---启动!---启动!---启动!'*2+'---')
torch.manual_seed(1)
numpy.random.seed(1)
初始化数据和神经网络 :
print('\n|' + '-' * 80 + '\n')
print('开始初始化数据和神经网络:\n')
batch_size = 8
print("\t开始读取用来训练 (名字) 的训练集......")
train_shuju = ShuJuJi("训练数据.csv")
xun_lian = torch.utils.data.DataLoader(train_shuju, batch_size=batch_size, shuffle=True)
print('\n\t\t OK!读取完成!')
print("\n\t开始读取用来测试(名字)的测试集......")
test_shuju = ShuJuJi("测试数据.csv")
ce_shi = torch.utils.data.DataLoader(test_shuju, batch_size=batch_size, shuffle=True)
print('\n\t\t OK!读取完成!')
print("\n\t激活(名字)全连接BP神经网络二分类器......")
moxing = MoXing().to(device)
初始化训练模式 :
print('\n|' + '-' * 80+'\n')
print('初始化训练模式:\n')
moxing.train() # 设置训练模式
xuexilv = 0 #学习率根据需求自己设置
loss_func = torch.nn.BCELoss() #损失函数
yiuhuaqi = torch.optim.SGD(moxing.parameters(),lr=xuexilv)
epochs = 0 #根据需求自己设置
xianshi = 0 #根据需求自己设置,即每xianshi个epoch显示一次
print(f"\t损失函数: {loss_func}")
print(f"\n\t优化器: {yiuhuaqi.__class__.__name__}")
print(f"\n\t学习率: {xuexilv}")
print(f"\n\tBatch size(每次迭代样本数): {batch_size}")
print(f"\n\tepochs(预遍历次数): {epochs}")
训练神经网络:
print('\n|' + '-' * 80+'\n')
print("开始训练(名字)神经网络:\n")
for epoch in range(0, epochs):
train_loss = 0.0
test_loss = 0.0
for (batch_index, batch) in enumerate(ce_shi):
X2 = batch[0]
Y2 = batch[1]
Y_yuce2 = moxing(X2)
loss_zhi2 = loss_func(Y_yuce2, Y2)
test_loss += loss_zhi2.item()
loss_test = test_loss/len(ce_shi) # 计算平均测试损失
pinggu_test = ping_gu(moxing, test_shuju, fenjie=0.5)
for (batch_index, batch) in enumerate(xun_lian):
X = batch[0]
Y = batch[1]
Y_yuce = moxing(X)
loss_zhi = loss_func(Y_yuce, Y)
train_loss += loss_zhi.item() # 累加张量的值
yiuhuaqi.zero_grad() # 重置所有梯度
loss_zhi.backward() # 计算新梯度
yiuhuaqi.step() # 更新所有权重
loss_train = train_loss/len(xun_lian) # 计算平均训练损失
if 0 < pinggu_test[1] < 200:
# 当神经网络的测试指标满足一定条件时保存神经网络
banben = str(datetime.datetime.today()).replace('-', '').replace(':', '').replace(' ', '')[:12]
path = f".\\Models\\T_{int(pinggu_test[1])}_{int(1000 * pinggu_test[2])}_{int(100000 * pinggu_test[2] / pinggu_test[1])}.pt"
torch.save(moxing.state_dict(), path)
print(f'\tOK! 已保存完成! 保存于 {path} ')
if epoch % xianshi == 0:
# 每进行xianshi个epoch进行显示
print("epoch = %5d loss_train = %8.7f loss_test = %8.7f" %(epoch,loss_train,loss_test),end='')
print(" zb1 = %0.5f zb2 = %5.1f zb3 = %0.5f zb_four = %0.5f" % (pinggu_test[0], pinggu_test[1], pinggu_test[2], pinggu_test[3]))
print("\t训练完成")
评估神经网络 :
print('\n|' + '-' * 80+'\n')
print("开始评估(名字)神经网络:\n")
moxing.eval()
pinggu_train = ping_gu(moxing, train_shuju, fenjie=0.5)
print("\t训练数据分类效果指标: ")
print("\t\t zb1 = %0.5f " % pinggu_train[0])
print("\t\t zb2 = %5.1f " % pinggu_train[1])
print("\t\t zb3 = %0.5f " % pinggu_train[2])
print("\t\t zb_four= %0.5f " % pinggu_train[3])
pinggu_test = ping_gu(moxing, test_shuju, fenjie=0.5)
print("\t测试数据分类效果指标: ")
print("\t\t zb1 = %0.5f " % pinggu_test[0])
print("\t\t zb2 = %5.1f " % pinggu_test[1])
print("\t\t zb3 = %0.5f " % pinggu_test[2])
print("\t\t zb_four= %0.5f " % pinggu_test[3])
保存神经网络 :
print('\n|' + '-' * 80+'\n')
print("开始保存训练后的 T 二分类器(保存 T 二分类器的训练状态):\n ")
banben=str(datetime.datetime.today()).replace('-','').replace(':','').replace(' ','')[:12]
#获取当前时间精确到分钟
path = f".\\Models\\T_model1.0_{banben}.pt"
#保存路径,也可以自己定名字或命名方法
torch.save(moxing.state_dict(), path)
print(f'\tOK! 已保存完成! 保存于 {path} ')
使用神经网络进行二分类 :
print('\n|'+'-'*80+'\n')
print("尝试使用(名字)二分类器分类预测:\n")
yorn = input('是否开始预测( y 或 n ):')
if yorn == 'y':
while True:
print('(x的各列列名)')
print('请按上述顺序输入特征参数(参数间以单个空格隔开):')
shu_ru_ji = [float(i) for i in input().split()]
if len(shu_ru_ji) == shuru:
xx = numpy.array([shu_ru_ji],dtype=numpy.float32)
xx = torch.tensor(xx, dtype=torch.float32).to(device)
moxing.eval()
with torch.no_grad():
Y_yuce = moxing(xx) # a Tensor
y_yucei = Y_yuce.item() # scalar, [0.0, 1.0]
print("输出结果: %0.5f " % y_yucei)
if y_yucei < 0.5:
print("\t预测为: 0")
else:
print("\t预测为: 1")
mmn = input('是否继续(输入 y 或 n ):')
if mmn == 'n':
break
print('分类结束.')
print("\n T 二分类器 当前进程结束.")
else:
print('\n T 二分类器 当前进程结束.')
备注:
使用保存的神经网络 :
def yuce(model_path,shu_ru):
model = MoXing().to(device)
model.load_state_dict(torch.load(mo_xing))
model.eval()
xx = torch.tensor(shu_ru, dtype=torch.float32).to(device)
with torch.no_grad():
yy = model(xx)
y_yuce = yy.item()
return int(y_yuce+0.5) #四舍五入,也可直接y_yuce
cd = pandas.read_csv('要用模型预测的表.csv', encoding="utf-8")
y=list(cd['y'])
models_list=[]
model_name=''
yuce=[]
for hang in range(len(y)):
shuRu = cd[hang:hang + 1] #第hang+1行的数据
shuRu = numpy.array(shuRu, dtype=numpy.float32)[:,1:]
#我的文件,第一列是y,特征从第二列开始,所以是[:,1:]
y_yuce=yuce(model_name, shuRu)
yuce.append(y_yuce)
#y_yuce为预测值
cd[model_name+'预测']=yuce
cd.to_csv('要用模型预测的表.csv',index=False)
训练模式二:
神经网路可视化训练
随着训练动态显示神经网络评估指标变化
即随着训练,动态显示神经效果变化
(这种模式更直观显示训练过程, 但会降低训练速度,且不再需要xianshi这个参数)
先引入plt :
( 要在上面“引入库”中多引入一个plt来画图 )
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False
动态可视化训练 :
(可将下文tu_.....列表换成你要画的数据列表)
print('\n|' + '-' * 80+'\n')
print("开始训练(名字)神经网络:\n")
tu_test_loss=[]
tu_train_loss=[]
tu_train_yuce=[]
tu_test_yuce=[]
tu_train_day=[]
tu_test_day=[]
tu_train_re=[]
tu_test_re=[]
tu_x=[]
for epoch in range(0, epochs):
tu_x.append(epoch)
train_loss = 0.0 # 一次完整迭代 enumerate将值变为索引:值
test_loss = 0.0
for (batch_index, batch) in enumerate(ce_shi):
X2 = batch[0]
Y2 = batch[1]
Y_yuce2 = moxing(X2)
loss_zhi2 = loss_func(Y_yuce2, Y2) # y 与 Y_yuce 仅有1个值,loss_zhi为仅有1值的张量
test_loss += loss_zhi2.item()
loss_test = test_loss/len(ce_shi) # 计算平均测试损失
tu_test_loss.append(loss_test)
pinggu_test = ping_gu(moxing, test_shuju, fenjie=0.5)
tu_test_yuce.append(pinggu_test[1])
tu_test_day.append(pinggu_test[2])
tu_test_re.append(pinggu_test[3])
for (batch_index, batch) in enumerate(xun_lian):
X = batch[0]
Y = batch[1]
Y_yuce = moxing(X)
loss_zhi = loss_func(Y_yuce, Y) # y 与 Y_yuce 仅有1个值,loss_zhi为仅有1值的张量
train_loss += loss_zhi.item() # 累加张量的值
yiuhuaqi.zero_grad() # 重置所有梯度
loss_zhi.backward() # 计算新梯度
yiuhuaqi.step() # 更新所有权重
loss_train = train_loss/len(xun_lian) # 计算平均训练损失
pinggu_train = ping_gu(moxing, train_shuju, fenjie=0.5)
tu_train_loss.append(loss_train)
tu_train_yuce.append(pinggu_train[1])
tu_train_day.append(pinggu_train[2])
tu_train_re.append(pinggu_train[3])
if epoch > 0:
plt.clf()
plt.subplot(2, 2, 1)
plt.plot(tu_x[1:], tu_train_loss[1:], label="训练误差", linestyle='-')
plt.plot(tu_x[1:], tu_test_loss[1:], label="测试误差", linestyle='-')
# plt.xlabel("遍历次数")
# plt.ylabel("误差", fontsize=16)
plt.title(f"训练误差 vs 测试误差")
plt.legend(loc='center left')
plt.subplot(2, 2, 2)
plt.plot(tu_x[1:], tu_train_day[1:], label="训练指标3", linestyle='-')
plt.plot(tu_x[1:], tu_test_day[1:], label="测试指标3", linestyle='-')
# plt.xlabel("遍历次数")
# plt.ylabel("指标3", fontsize=16)
plt.title(f"训练指标3 vs 测试指标3")
plt.tight_layout()
plt.legend(loc='center left')
plt.subplot(2, 2, 3)
plt.plot(tu_x[1:], tu_train_yuce[1:], label="训练预测", linestyle='-')
plt.plot(tu_x[1:], tu_test_yuce[1:], label="测试预测", linestyle='-')
# plt.xlabel("遍历次数")
# plt.ylabel("预测", fontsize=16)
plt.title(f"训练预测 vs 测试预测")
plt.tight_layout()
plt.legend(loc='center left')
plt.subplot(2, 2, 4)
plt.plot(tu_x[1:], tu_train_re[1:], label="训练占比率", linestyle='-')
plt.plot(tu_x[1:], tu_test_re[1:], label="测试占比率", linestyle='-')
# plt.xlabel("遍历次数")
# plt.ylabel("占比率", fontsize=16)
plt.title(f"训练占比率 vs 测试占比率")
plt.tight_layout()
plt.legend(loc='center left')
plt.pause(0.5) # 暂停一段时间,不然画的太快会卡住显示不出来
plt.ioff() # 关闭画图窗口
未完待续
如果对您有帮助,请多多支持。