用python不调用其他库实现BP神经网络

这段时间做了一个二分类的任务,训练数据是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()

你可能感兴趣的:(机器学习,深度学习,机器学习)