剖析正向传播&反向传播_单层神经网络(一)

1.制作数据

1.1 h5py简介

"""
训练数据集和测试数据集为:train_catvnoncat.h5、train_catvnoncat.h5
本例打开训练数据集中训练数据的第一个数据(更改索引号,查看其它数据)。测试集中测试数据同样课通过该方法打开
"""
import h5py  # 工具
from PIL import Image

def read():
    traindata = h5py.File("datasets/train_catvnoncat.h5", mode="r")  # File:类
    for key, value in traindata.items():
        print(key, value)
    datalist = traindata["train_set_x"]
    print(datalist)
    print(datalist.shape)
    print(type(datalist[0]))
    print(datalist[0])
    image = Image.fromarray(datalist[5])#包含正样本和负样本
    image.show()


    # testdata=h5py.File("datasets/train_catvnoncat.h5","r")
    # for key,value in testdata.items():
    #     print(key,value)


read()

1.2制作数据

#最终代码。(一)
import h5py


class Mydata():

    def __init__(self,train_path,test_path):
        self.traindata = h5py.File(train_path, "r")#File:类
        self.testdata = h5py.File(test_path, "r")

    def get_traindata(self):
        get_traindataX = self.traindata["train_set_x"][:]/255.-0.5#归一化、去均值
        get_traindataY = self.traindata["train_set_y"]
        return get_traindataX, get_traindataY

    def get_testdata(self):
        get_testdataX = self.testdata["train_set_x"][:]/255.-0.5#归一化、去均值
        get_testdataY = self.testdata["train_set_y"]
        return get_testdataX,get_testdataY

2.制作网络

2.1 Sigmoid函数

2.1.1函数曲线

在这里插入图片描述

2.1.2函数公式

在这里插入图片描述

2.1.3函数导数

在这里插入图片描述

2.1.4代码实现

#源码
import numpy as np
def Sigmoig(z):
    a=1/(1+np.exp(-z))
    return a

2.2损失函数

2.2.1链式调用

#实例
class Person:
    def name(self, name):
        self.name = name
        return self

    def age(self, age):
        self.age = age
        return self

    def show(self):
        print("My name is", self.name, "and I am", self.age, "years old.")

p = Person()
p.name("Li Lei").age(15).show()
#打印结果
My name is Li Lei and I am 15 years old.

2.2.2单神经元反向求导

noncat 或cat

(1)输入矩阵X维度

​ 假设输入图片维度为(64,64, 3),转化为一维特征向量为(12288,1)。 此特征向量x是列向量,维度一般记为n_x 。

	如果训练样本共有m张图片,那么整个训练样本X组成了矩阵,维度是(n_x,m) 。其中。n_x 代表了每个样本x^(i)特征个数 , 列m代表了样本个数。  Andrew解释 该形式矩阵方便后边矩阵运行方便。

(2)权重矩阵W维度

(n_x ,1)

(3)常数b维度

常数

(4)输出矩阵Z维度

(1,m)

(5)计算维度分析

前向传播:
Z ( i ) = W T X ( i ) + B Z^{(i)} =W^TX^{(i)}+B

( 1 , m ) = ( n x , 1 ) T n x m + B = ( 1 , n x ) n x m ) + B 矩阵变化情况:(1,m)=(n_x ,1)^T*(n_x,m)+B=(1,n_x)*(n_x,m))+B

A ( i ) = s i g m o i d ( Z ( i ) ) A^{(i)}=sigmoid(Z^{(i)})

( 1 , m ) = ( 1 , m ) 矩阵变化情况:(1,m)=(1,m)

L = ( A ( i ) Y ) 2 / m L=(A^{(i)}-Y)^2/m

( 1 , m ) = ( ( 1 , m ) ( 1 , m ) ) 2 / m 矩阵变化情况:(1,m)=((1,m)-(1,m))^2/m

反向求导:
d A ( i ) = 2 ( A ( i ) Y ) dA^{(i)}=2(A^{(i)}-Y)

d A : ( 1 , m ) 矩阵变化情况:dA:(1,m)

d Z ( i ) = d A ( i ) ( A ( i ) ( 1 A ( i ) ) ) dZ^{(i)}=dA^{(i)}*(A^{(i)}(1-A^{(i)}))

d Z : ( 1 , m ) 矩阵变化情况:dZ:(1,m)

d W = 1 m X d Z ( i ) . T dW=\frac{1}{m}X* dZ^{(i)}.T

d W 1 1 X d Z ( i ) . T W 矩阵变化情况:dW:(1,1)。X* dZ^(i).T为梯度的和。除以总样本数,得到平均梯度。平均梯度时对当前每个W参数的所有样本的平均梯度。

d B = 1 m i = 1 m d Z ( i ) dB=\frac{1}{m}\sum_{i=1}^m dZ^{(i)}

d B : 1 , 1 矩阵变化情况:dB:(1,1)

(6)代码实现

#源码
import numpy as np
from MyData import Dataset
import matplotlib.pyplot as plt
import pickle
from PIL import Image

def sigmoid(z):
    a = 1 / (1 + np.exp(-z))
    return a

class SingleNeural:
    cache = []
    def __init__(self, in_feature=None ,bias=True):

        self.weight = np.random.randn(in_feature, 1) * 0.01
        if bias:
            self.bias = 0

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

    def forward(self, x):
        x = x.reshape(-1, 64*64*3)
        x = x.T # 先转置一下
        SingleNeural.cache.append(x) # 保存x参数
        z = np.dot(self.weight.T, x)
        if hasattr(self, "bias"):
            z += self.bias
        a = sigmoid(z)
        return a

class MSELoss:
    def __call__(self, output, target):
        self.A = output
        self.Y = target
        self.m = target.shape[0]
        return self.forward(output,target)

    def forward(self, A, Y):
        self.loss = np.sum(np.square(A - Y)) / self.m
        return self

    def float(self):
        return self.loss

    def backward(self):
        dA = 2 * (self.A - self.Y)
        dZ = dA * (self.A * (1 - self.A)) # sigmoid的导数过来的
        x = SingleNeural.cache[0]
        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, lr=0.01):

        self.net = net
        self.lr = lr

    def step(self, grads):

        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"]
        # 清空参数
        SingleNeural.cache.clear()

2.3制作优化器

2.2.1梯度下降

​ J(w,b)是convex function,梯度下降算法是先随机选择一组参数w和b值,然后每次迭代的过程中分别沿着w和b的梯度(偏导数)的反方向前进一小步,不断修正w和b。每次迭代更新w和b后,都能让J(w,b)更接近全局最小值。梯度下降的过程如下图所示。

在这里插入图片描述

​ 梯度下降算法每次迭代更新,w和b的修正表达式为:
在这里插入图片描述
在这里插入图片描述
原文链接: https://blog.csdn.net/red_stone1/article/details/77851177

2.2.2代码实现

#源代码
class Optimizer:

    def __init__(self, net, lr=0.01):
        self.net = net
        self.lr = lr

    def step(self, grads):

        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"]
        # 清空参数
        SingleNeural.cache.clear()

2.4单个感知机制作

2.4.1call()

2.4.1.1概念
	Python中有一个有趣的语法,只要定义类型的时候,实现<u>__</u>call__函数,这个类型就成为可调用的。换句话说,我们可以把这个类型的对象当作函数来使用,相当于 重载了括号运算符。 
2.4.1.2示例

​ 所有的函数都是可调用对象。一个类实例也可以变成一个可调用对象,只需要实现一个特殊方法call ,我们把 Person 类变成一个可调用对象:

#实例
class Person(object):
    def __init__(self, name, gender):
        self.name = name
        self.gender = gender
    def __call__(self, friend):
        print('My name is %s...' % self.name)
        print('My friend is %s...' % friend)
        
p=Person('Bob', 'male')
p('Tim')
#打印结果
My name is Bob...
My friend is Tim...

​ 单看 p(‘Tim’) 你无法确定 p 是一个函数还是一个类实例,所以,在Python中,函数也是对象,对象和函数的区别并不显著。

2.4.2代码实现

#源码
class SingleNeural:
    cache = []
    def __init__(self, in_feature=None ,bias=True):
        self.weight = np.random.randn(in_feature, 1) * 0.01
        if bias:
            self.bias = 0

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

    """前向传播_后前部分"""
    def forward(self, x):
        x = x.reshape(-1, 64*64*3)
        x = x.T 
        SingleNeural.cache.append(x) # 保存x参数
        z = np.dot(self.weight.T, x)
        if hasattr(self, "bias"):
            z += self.bias
        a = sigmoid(z)
        return a

2.5制作训练模型

2.5.1序列化与反序列化

#实例
import pickle
class User:
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def to_string(self):
        return self.name , self.age

user=User("张三",20)
print(user.to_string())
#序列化对象
with open("user.pth","wb") as f:
    pickle.dump(user,f)#生成一个user.pth文件
# 反序列化
with open("user.pth","rb") as f:
    user=pickle.load(f)#加载对象
    print(user.to_string())

2.5.2代码实现

#源码
class Trainer:

    def __init__(self):
        self.net = SingleNeural(64*64*3)
        self.loss_func = MSELoss()
        self.opt = Optimizer(self.net, lr=0.02)
        dataset = Dataset("datasets/train_catvnoncat.h5","datasets/test_catvnoncat.h5")
        self.trainset = dataset.get_train_set()
        self.testset = dataset.get_test_set()

    def save(self, net ,path):#保存网络结构
        with open(path, "wb") as f:
            data = pickle.dumps(net)
            f.write(data)

    def load(self, path):#加载网络结构
        with open(path, "rb") as f:
            data = f.read()
            net = pickle.loads(data)
        return net

    def train(self):
        x , y = self.trainset[0] , self.trainset[1]
        print(x.shape)
        losses = []
        for i in range(10000):
            out = self.net(x)
            loss = self.loss_func(out,y)
            if i % 100 == 0:
                print("{}/{},loss:{}".format(i,5000, loss.float()))
                losses.append(loss.float())
                plt.clf()
                plt.plot(losses)
                plt.pause(0.01)

            grads = loss.backward()
            self.opt.step(grads)
            self.save(self.net, "models/net.pth")

    def test(self):
        x, y = self.testset[0], self.testset[1]
        net = self.load("models/net.pth")
        # for i in range(x.shape[0]):
        #     img = ((x[i] + 0.5) * 255).astype(np.uint8)
        #     img = Image.fromarray(img)
        #     out = net(x[i])
        #     plt.clf()
        #     plt.axis("off")
        #     plt.text(0,-5,"The result of AI: {}".format("cat" if out.round() > 0 else "non-cat" ), color="red")
        #     plt.imshow(img)
        #     plt.pause(1)
        """
         正负样本总共预测对了多少个
        """
        print(y)
        out = net(x)
        print(out.round())
        predict = (out.round() == y).sum()
        print("预测对了{}个".format(predict))

    def test_other(self):
        img = Image.open("images/0.57.jpeg")
        img = img.resize((64,64),Image.ANTIALIAS)
        img = np.array(img) / 255. - 0.5
        net = self.load("models/net.pth")
        out = net(img)
        print(out)
        print(out.round())

2.6最终代码

#网络总代码(二)
import numpy as np
from MyData import Dataset
import matplotlib.pyplot as plt
import pickle
from PIL import Image

def sigmoid(z):
    a = 1 / (1 + np.exp(-z))
    return a

class SingleNeural:
    cache = []
    def __init__(self, in_feature=None ,bias=True):
        """
        初始化神经元的参数
        :param in_feature: 输入的特征数
        :param bias: 是否要偏置
        """
        self.weight = np.random.randn(in_feature, 1) * 0.01#w越靠近0,梯度更新越快。
        if bias:
            self.bias = 0

    def __call__(self, x):
        """
        模仿Pytorch的风格
        :param x:
        :return:
        """
        return self.forward(x)

    def forward(self, x):
        """
        前向运算 动态构建网络的结构拓扑图
        :param x: 输入的数据
        :return: 结果
        """
        x = x.reshape(-1, 64*64*3)
        x = x.T # 先转置一下
        SingleNeural.cache.append(x) # 保存x参数
        z = np.dot(self.weight.T, x)
        if hasattr(self, "bias"):
            z += self.bias
        a = sigmoid(z)
        return a

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

    def forward(self, A, Y):
        self.loss = np.sum(np.square(A - Y)) / self.m
        # 链式编程风格
        return self

    def float(self):
        return self.loss

    def backward(self):
        """
        方向计算梯度,求导,
        BP算法的核心代码
        求导均是求 loss对参数的导数
        此处运用了高数的链式法则
        dL/dW = dL/dA * dA/dZ * dZ/dW
        :return:
        """
        dA = 2 * (self.A - self.Y)
        dZ = dA * (self.A * (1 - self.A)) # sigmoid的导数过来的
        x = SingleNeural.cache[0]
        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, lr=0.01):
        """
        初始化优化器的步长
        :param net:
        :param lr:
        """
        self.net = net
        self.lr = lr

    def step(self, grads):

        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"]
        # 清空参数
        SingleNeural.cache.clear()

class Trainer:

    def __init__(self):
        self.net = SingleNeural(64*64*3)
        self.loss_func = MSELoss()
        self.opt = Optimizer(self.net, lr=0.02)
        dataset = Dataset("datasets/train_catvnoncat.h5","datasets/test_catvnoncat.h5")
        self.trainset = dataset.get_train_set()
        self.testset = dataset.get_test_set()

    def save(self, net ,path):
        """
        保存网络结构
        :param net:
        :param path:
        :return:
        """
        with open(path, "wb") as f:
            data = pickle.dumps(net)
            f.write(data)

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

    def train(self):
        x , y = self.trainset[0] , self.trainset[1]
        print(x.shape)
        losses = []
        for i in range(10000):
            out = self.net(x)
            loss = self.loss_func(out,y)
            if i % 100 == 0:
                print("{}/{},loss:{}".format(i,5000, loss.float()))
                losses.append(loss.float())
                plt.clf()
                plt.plot(losses)
                plt.pause(0.01)

            grads = loss.backward()
            self.opt.step(grads)
            self.save(self.net, "models/net.pth")

    def test(self):
        x, y = self.testset[0], self.testset[1]
        net = self.load("models/net.pth")
        # for i in range(x.shape[0]):
        #     img = ((x[i] + 0.5) * 255).astype(np.uint8)
        #     img = Image.fromarray(img)
        #     out = net(x[i])
        #     plt.clf()
        #     plt.axis("off")
        #     plt.text(0,-5,"The result of AI: {}".format("cat" if out.round() > 0 else "non-cat" ), color="red")
        #     plt.imshow(img)
        #     plt.pause(1)
        """
         正负样本总共预测对了多少个
        """
        print(y)
        out = net(x)
        print(out.round())
        predict = (out.round() == y).sum()#out.round():四舍五入
        print("预测对了{}个".format(predict))

    def test_other(self):
        img = Image.open("images/0.57.jpeg")
        img = img.resize((64,64),Image.ANTIALIAS)
        img = np.array(img) / 255. - 0.5
        net = self.load("models/net.pth")
        out = net(img)
        print(out)
        print(out.round())
if __name__ == '__main__':

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

3.测试

#最终代码(三)
import h5py
from PIL import Image

def read():
    trainData = h5py.File("datasets/train_catvnoncat.h5",mode="r")
    for key , value in trainData.items():
        print(key, value) # cat non_cat
    datalist = trainData["train_set_x"]
    print(datalist[:])
    img = Image.fromarray(datalist[2])
    # img.show()
    target = trainData["train_set_y"]
    print(target[:])

read()

四.总结

4.1最终代码

最终三个代码块:(一)(二)(三)。

4.2最终数据

数据:https://pan.baidu.com/s/1Tix5CRLVXjQO6tkPO_LiFw

发布了29 篇原创文章 · 获赞 45 · 访问量 5054

猜你喜欢

转载自blog.csdn.net/sinat_39783664/article/details/103160901