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
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