这段时间做了一个二分类的任务,训练数据是8000个包含1000个特征和一个label的数据。下面记录一下使用python不调用其他库实现BP神经网络二分类的代码。
import os
import sys
import time
import random
import numpy as np
# 获取路径
def get_directory(file_name):
path = os.getcwd()
directory = os.path.join(path,file_name)
return directory
# 读取速度相对较慢
def get_train_data(directory):
data = np.loadtxt(directory,delimiter=',')
print(data.shape)
# 读取速度相对更快
def loadDataSet(file_name, label_existed_flag):
feats = []
labels = []
fr = open(file_name)
lines = fr.readlines()
for line in lines:
temp = []
allInfo = line.strip().split(',')
dims = len(allInfo)
if label_existed_flag == 1:
for index in range(dims - 1):
temp.append(float(allInfo[index]))
feats.append(temp)
labels.append(float(allInfo[dims - 1]))
else:
for index in range(dims):
temp.append(float(allInfo[index]))
feats.append(temp)
fr.close()
feats = np.array(feats)
labels = np.array(labels)
return feats, labels
# 读取tset的label
def loadLabels(file_name):
labels = []
fr = open(file_name)
lines = fr.readlines()
for line in lines:
allInfo = line.strip().split(',')
labels.append(float(allInfo[0]))
fr.close()
labels = np.array(labels)
return labels
class network:
def __init__(self,sizes,train_data,test_data,minibatch_num,train_num,learning_rate,print_num):
self.layer_num = len(sizes)
self.size = sizes
self.train_data = train_data
self.test_data = test_data
self.minibatch_num = minibatch_num
self.train_num = train_num
self.learning_rate = learning_rate
self.print_num = print_num
self.initialize_parameter()
# 初始化权重
def initialize_parameter(self):
# weight和bias为层数-1个数组组成的list
self.weight = [np.random.randn(next_neuron_num,last_neuron_num)/np.sqrt(last_neuron_num)
for next_neuron_num,last_neuron_num in zip(self.size[1:],self.size[:-1])]
self.bias = [np.random.randn(neuron_num) for neuron_num in self.size[1:]]
# 将数据转换为minibatch的形式
def get_minibatch(self):
data = self.train_data.copy()
random.shuffle(data)
minibatch_data = [data[k:k+self.minibatch_num] for k in range(0,len(self.train_data),self.minibatch_num)]
return minibatch_data
# 运行网络的前向
def forward(self,input):
for weight,bias in zip(self.weight,self.bias):
z = np.dot(weight,input) + bias
input = self.sigmoid(z)
return input
# 训练一次
def backward_once(self,x,y):
record_derivatives_w = [np.zeros(w.shape) for w in self.weight]
record_derivatives_b = [np.zeros(b.shape) for b in self.bias]
record_neuron_z = []
record_neuron = [x]
# 运行网络前向
for weight,bias in zip(self.weight,self.bias):
z = np.dot(weight,x)
record_neuron_z.append(z)
x = self.sigmoid(z)
record_neuron.append(x)
# 记录交叉熵和差值
cross_entropy = self.cross_entropy(record_neuron[-1],y)
delta = self.delta(record_neuron[-1],y)
# 记录最后一层的权值导数
record_derivatives_b[-1] = delta
record_derivatives_w[-1] = np.dot(np.transpose([delta]),[record_neuron[-2]])
# 反向传播
for layer in range(1,self.layer_num-1):
# 计算对sigmoid函数的导数,最后一层对sigmoid的导数在上面与cross_entropy一起计算过
derivative_sigmoid = self.sigmoid_prime(record_neuron_z[-layer-1])
# 计算对神经元sigmoid前z的导数
delta = np.dot(np.transpose(self.weight[-layer]),delta) * derivative_sigmoid
# 计算对阈值的导数
record_derivatives_b[-layer-1] = delta
# 计算对权重的导数
record_derivatives_w[-layer-1] = np.dot(np.transpose([delta]),[record_neuron[-layer-2]])
return record_derivatives_w,record_derivatives_b,cross_entropy
def train_once_minibatch(self,minibatch_data):
sum_cross_entropy = 0
record_sum_derivative_w = [np.zeros(temp_weight.shape) for temp_weight in self.weight]
record_sum_derivative_b = [np.zeros(temp_bias.shape) for temp_bias in self.bias]
for input,label in minibatch_data:
derivatives_w, derivatives_b, cross_entropy = self.backward_once(input,label)
record_sum_derivative_w = [last_dw+new_dw for last_dw,new_dw in zip(record_sum_derivative_w, derivatives_w)]
record_sum_derivative_b = [last_db+new_db for last_db,new_db in zip(record_sum_derivative_b, derivatives_b)]
sum_cross_entropy += cross_entropy
count = 0
for d_weight,d_bias in zip(record_sum_derivative_w,record_sum_derivative_b):
self.weight[count] -= self.learning_rate * d_weight/self.minibatch_num
self.bias[count] -= self.learning_rate * d_bias/self.minibatch_num
count += 1
print(sum_cross_entropy)
return sum_cross_entropy
# 训练
def train(self):
# 训练次数
for i in range(self.train_num):
print(i)
# 记录一次训练的损失
sum_entropy = 0
# 打乱数据集,获取minibatch数据
minibatch_dataset = self.get_minibatch()
# 一次取一个minibatch
for minibatch_data in minibatch_dataset:
# 训练,更新参数
entropy = self.train_once_minibatch(minibatch_data)
sum_entropy += entropy
sum_entropy /= len(minibatch_dataset)
# if i % self.print_num == 0:
train_accuracy = self.get_accuracy(self.train_data)
test_accuracy = self.get_accuracy(self.test_data)
print("第",i,"次训练","损失为:",sum_entropy,"训练准确率为:",train_accuracy,"测试准确率为:",test_accuracy)
# 计算训练集的准确率
def get_accuracy(self,train_data):
right_num = 0
for x,y in train_data:
output = self.forward(x)
if output > 0.5:
if y == 1:
right_num += 1
else:
if y == 0:
right_num += 1
accuracy = right_num / len(train_data)
return accuracy
# 计算差值
def delta(self,x,y):
return (x-y)
# 交叉熵函数,x为预测值,y为label值
def cross_entropy(self,x, y):
return np.sum(np.nan_to_num(-y*np.log(x)-(1-y)*np.log(1-x)))
# sigmoid函数
def sigmoid(self,x):
return 1.0/(1.0+np.exp(-x))
# sigmoid函数的导数
def sigmoid_prime(self,x):
return self.sigmoid(x) * (1 - self.sigmoid(x))
if __name__ == "__main__":
# 计时
time1 = time.time()
# 输入文件名
train_file_name = "train_data.txt"
test_data_name = "test_data.txt"
test_label_name = "answer.txt"
# 获取绝对路径
train_directory = get_directory(train_file_name)
test_data_directory = get_directory(test_data_name)
test_label_directory = get_directory(test_label_name)
# 加载数据
train_feats, train_labels = loadDataSet(train_directory,1)
test_feats, test_labels = loadDataSet(test_data_directory,0)
real_test_label = loadLabels(test_label_directory)
# 将数据转换为神经网络输入格式
train_dataset = list(zip(train_feats, train_labels))
test_dataset = list(zip(test_feats, real_test_label))
# 设置神经网络参数
sizes = [1000,512,1]
minibatch_num = 10
train_num = 100
learning_rate = 0.1
print_num = 10
my_network = network(sizes,train_dataset,test_dataset,minibatch_num,train_num,learning_rate,print_num)
my_network.train()