手推BP算法系列1——Python实现单神经元网络(Pyrtoch框架)

1.获取数据集MyData.py
此数据集为含有猫和非猫的数据集,训练集209张,测试集50张

import h5py
import matplotlib.pyplot as plt

class MyDataset:
    def __init__(self):
        self.train = h5py.File("datasets/train_catvnoncat.h5")
        self.test = h5py.File("datasets/test_catvnoncat.h5")
        # print(self.train.keys())#键 #
        # print(self.train["train_set_x"])#ndarray,可以通过切片获得数据的值
        # print(self.train["train_set_x"][:])
        """看图片
        x = self.train["train_set_x"][:]
        plt.imshow(x[0])
        plt.show()
        
        x = self.train["train_set_x"][:]
        for e in x:
            plt.imshow(e)
            plt.pause(1)
        """
    def get_train_set(self):
        return self.train["train_set_x"][:] / 255.,self.train["train_set_y"][:]

    def get_test_set(self):
        return self.test["test_set_x"][:] / 255., self.test["test_set_y"][:]

if __name__ == '__main__':
    data = MyDataset()
    print(data.get_train_set()[1].shape)
    print(data.get_test_set()[0].shape)
    # 209

手推BP算法系列1——Python实现单神经元网络(Pyrtoch框架)_第1张图片
手推BP算法系列1——Python实现单神经元网络(Pyrtoch框架)_第2张图片

2.网络MyNet.py
单神经元网络加sigmoid激活函数。
思路:

1.首先要有权重。有多少权重就有多少特征并且初始化,1列w表示只又1个w,
偏移也可以设置并且可以设置为0,单神经元w也可以设置为0。梯度下降目的
就是为了找到最优解,所以权重初始化很有讲究,以防进入局部最小值。第二
个层面:随着网络层数增加,梯度在反向传播的过程中可能会消失,梯度消失
与w有关。第三个层面:学w也是学一种分布状态,不知道分布时,就假设是
正态分布。
此处优化手段是给权重乘以0.01:梯度消失:参数过大或过小(一般都是过小)
导致的。乘以0.01,思考网络的输出怎么来的,A1=W1X A2=W2A1 A3=W3A2
A3=W3W2W1X。梯度与w有关,w>1精度爆炸,w<1输出过小,反向越来越小,
造成梯度弥散,使w趋近于1最好。
W是很多w加起来的结果:
假设一种极端情况,A3=W1X+W2X+W3X=(W1+W2+W3)X
2.方法有前项计算。
z=w.T*x+b
x的形状(209,64,64,3)变NV(209,12288);w的形状则为(12288,1);
对x转置得(12288,209);故w.T*x形状为(1,209);
如果有b,则加入。
得到结果再激活。
3.计算损失
4.优化器
5.训练器
import numpy as np

def sigmoid(z):
    """
    active function
    :param z:
    :return:
    """
    return 1 / (1 + np.exp(-z))

class SingleNeuralNet:
    """
    单神经元网络
    """
    cache = [] # 保存动态图节点

    def __init__(self, in_features=None,bias=True):
    #in_features:多少的特征就有多少的权重
        self.weight = np.random.randn(in_features,1) * 0.01
        #假设正态分布,*0.01减小了梯度消失,很重要
        if bias:
            self.bias = 0

    def clear(self):
        SingleNeuralNet.cache.clear()

    def __call__(self, x):
        return self.forward(x)

    def forward(self, x):
        """
        前向计算
        z = w.T * x + b
        w: 12288 1
        x:209 12288
        1 12288 12288 209 = 1 209
        :param x:
        :return:
        """
        x = x.T
        # 记录节点
        SingleNeuralNet.cache.append(x)
        Z = np.dot(self.weight.T, x)
        if hasattr(self, "bias"):#如果有属性,判断有没有bias
            Z += self.bias
        A = sigmoid(Z)
        return A

class MSELoss:
    """
    均方差函数
    loss = 1/m *Σ(A - Y)^2
    """
    def __call__(self,output,target):
        self.m = target.shape[1]#批次
        self.A = output
        self.Y = target
        return self.forward(output,target)

    def forward(self,A,Y):
        self.loss = np.sum(np.square(A - Y)) / self.m
        return self# 返回self才可以调backward

    def float(self):
        return self.loss# 获取值

    def backward(self):
        """
        反向求导的核心代码
        用到了高数的导数知识
        求导数都是求损失对某一个变量的导数
        链式求导
        dA -> dZ -> dW
        dA -> dZ -> db
        :return:
        """
        dA = 2 * (self.A - self.Y)
        dA_dZ = self.A * (1 - self.A)
        dZ = dA * dA_dZ

       """
        dZ :dZ: 1 209 209是每张图片的导数 x.T :209 12288 
        dZ :  x: 12288 209  dZ.T :209 1 形状相同才能更新w(12288,1)	
        乘法的含义:x:每一行代表样本的第一个特征到最后一个
        特征,209列表示样本是竖着排的,矩阵乘法是每一行乘以
        每一列再相加,第一行是209个样本的特征,有很多
        dw,dw1=dz*x1,dw2=dz2*x1,累加起来得到dW,此处需要除
        以样本数,得到平均梯度
        网络拓扑图
        """
        x = SingleNeuralNet.cache[0]
        """
        x从哪来,pytorch基于动态图,神经网络节点构成动态网络拓扑图
        ,每个节点做前向时,才会添加到图里面去,因此需要记录每一个
        节点,在反向传播时进行计算
        """
        dW = np.dot(x, dZ.T) / self.m
        dB = np.sum(dZ) / self.m

        return {"DW":dW,"DB":dB}


class Optimizer:

    def __init__(self,net=None, lr=0.1):
        self.lr = lr
        self.net = net

    def step(self, grads):
        """
        更新参数
        :return:
        """
        self.net.weight = self.net.weight - self.lr * grads["DW"]
        if hasattr(self.net, "bias"):
            self.net.bias = self.net.bias - self.lr * grads["DB"]

        self.net.clear()
        """
        清空cache,节点是动态的,forward才添加进去,
        backward时需要清空,避免第二次获取元素时获取
        到上一个元素,即要保证x是最新的,不清空就会越
        来越多,与网络结构不匹配了(一个神经元只有一个输入)
        """
if __name__ == '__main__':

    s = SingleNeuralNet(128)
    print(s.weight)

3.训练Trainer.py

from MyNet import SingleNeuralNet,MSELoss,Optimizer
import numpy as np
from MyData import MyDataset
import matplotlib.pyplot as plt
import pickle

class Trainer:
    def __init__(self):

        self.net = SingleNeuralNet(64*64*3)
        self.loss_func = MSELoss()
        self.opt = Optimizer(self.net, lr=0.01)
        self.dataset = MyDataset()

    def train(self):
        x, y = self.dataset.get_train_set()
        #数据少时,做batch梯度下降
        x = x.reshape(-1, 64 * 64 * 3)
        y = y[np.newaxis, :]#y的形状是(209,)out(1,209)
        epochs = 5000
        losses = []
        for i in range(epochs):
            out = self.net(x)
            loss = self.loss_func(out,y)

            if i % 100 == 0:
                losses.append(loss.float())
                print("{}/{},loss:{}".format(i,
                epochs, loss.float()))
                plt.clf()
                plt.plot(losses)
                plt.pause(0.1)

            grads = loss.backward()
            self.opt.step(grads)

        self.save(self.net, "models/net.pth")

    def save(self,net,path):
	    """
	    保存就是持久化,写入硬盘,写的是网络,是字节文件
	    """
        with open(path, "wb") as f:
            pickle.dump(net,f)
        print("保存成功")


    def load(self, path):
        with open(path,"rb") as f:
            net = pickle.load(f)
        return net

    def test(self):
        x, y = self.dataset.get_test_set()
        net = self.load("models/net.pth")
        x = x.reshape(-1, 64*64*3)
        out = net(x)
        out = out.round()
        y = y[np.newaxis,:]
        accuracy = (out == y).mean() # 精度
        print(accuracy * 100 )
if __name__ == '__main__':

    t = Trainer()
    # t.train()
    t.test()

例子:读写二进制

"""
持久化
序列化与反序列化
oracle mysql mssql
"""
import pickle

student = {"name":"张三","age":23}

with open("stu", "wb") as f:
    pickle.dump(student,f)


with open("stu","rb") as f:
    stu = pickle.load(f)
    print(stu)

改进方向:激活函数、损失函数、设计w

数据处理:
1.归一化/标准化
2.设计网络(MLP)变NV形状
3.根据目的添加输出函数
4.训练网络
加载数据DataLoader(dataset,batch_size,shuffle)
Batch梯度下降
BGD梯度下降(数据全加载)
MBGD批量梯度下降
SGD随机梯度下降(一个一个样本传即批次等于1,效果不好,因为特征不能代表所有,并且有可能是错误的结果)
5.训练数据
epohs轮次
iteration迭代(取决于batch_size)
backward反向传播(学习参数)
save
6.测试
加载数据
forward出结果,跟标签比较,得到精度

数据集获取链接:
链接:猫和非猫的h5格式数据集
提取码:s4kp

你可能感兴趣的:(深度学习,pytorch,python)