[鸢尾花的种类]python
预测鸢尾花的种类
Iris数据集在RA Fisher于1936年发表的经典论文《分类问题中的多重测量的使用》中使用,也能够在UCI中找到。它包括三个种类,每一个种类有50个样本以及每朵花的一些特性。一种花能够与其余两种花是线性可分的,可是另两种不是线性可分的。
如图
算法
目标
分类
预测鸢尾花的种类。数组
进阶
想办法在预测模型上提升精度网络
说明
- 给出导入数据的方式
from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split iris = load_iris() x_train, x_test, y_train, y_test = train_test_split(iris.data, iris.target) x_train, y_train # train data x_test, y_test # test data
- 数据均是实数没有文本
- 请在训练集上训练模型,在测试集上进行预测
分析问题
图像的分类问题能够使用神经网络来写。
这里我建立了一个两层的神经网络Network
dom
交叉熵偏差
使用的是交叉熵偏差公式为
E = − 1 N ∑ n ∑ k t n k l o g y n k E = -\frac{1}{N}\sum_{n}\sum_{k}t_{nk}logy_{nk} E=−N1n∑k∑tnklogynk
对应的代码
ide
def cross_entropy_error(y, t): # y为测试标签,t为正确标签 if y.ndim == 1: t = t.reshape(1, t.size) y = y.reshape(1, y.size) # 监督数据是one-hot-vector的状况下,转换为正确解标签的索引 if t.size == y.size: t = t.argmax(axis=1) batch_size = y.shape[0] return -np.sum(np.log(y[np.arange(batch_size), t] + 1e-7)) / batch_size
sigmoid
我这个两层神经网络中使用的激活函数是 s i g m o i d ( x ) = 1 1 + e − x sigmoid(x) = \frac{1}{1+e^{-x}} sigmoid(x)=1+e−x1
在反向传播中他的导数为sigmoid(x)(1 - sigomid(x))
sigmoid在反向传播中的代码以下:
函数
class Sigmoid: def __init__(self): self.out = None def forward(self, x): out = sigmoid(x) self.out = out return out def backward(self, dout): dx = dout * (1.0 - self.out) * self.out return dx
其中forward返回正向传播的值,backward返回反向传播的结果。
由于在反向传播的时候须要他的输出变量,因此在正向传播的时候要将输出值out存储在实例变量out中。
学习
Affine
反向传播中Affine层为 W*X + B,由于W,X,B均为矩阵,因此运用到了numpy中的dot函数,np.dot(X, W) 表示矩阵X和W相乘(矩阵X * W != W * X)
方向传播能够写为(T表明矩阵转置的意思):
∂ L ∂ X = ∂ L ∂ Y ⋅ W T \frac{\partial L}{\partial X} = \frac{\partial L}{\partial Y} · W^T ∂X∂L=∂Y∂L⋅WT
∂ L ∂ Y = X T ⋅ ∂ L ∂ Y \frac{\partial L}{\partial Y} = X^T·\frac{\partial L}{\partial Y} ∂Y∂L=XT⋅∂Y∂L
因此在反向传播中Affine层所对应的代码以下:
测试
class Affine: def __init__(self, W, b): self.W = W self.b = b self.x = None self.original_x_shape = None # 权重和偏置参数的导数 self.dW = None self.db = None def forward(self, x): # 对应张量 self.original_x_shape = x.shape x = x.reshape(x.shape[0], -1) self.x = x out = np.dot(self.x, self.W) + self.b return out def backward(self, dout): dx = np.dot(dout, self.W.T) self.dW = np.dot(self.x.T, dout) self.db = np.sum(dout, axis=0) dx = dx.reshape(*self.original_x_shape) # 还原输入数据的形状(对应张量) return dx
其中forward返回正向传播的值,backward返回反向传播的结果。(在两层的神经网络中他须要用到两次~)spa
Softmax-With-Loss
Softmax
在介绍Softmax-With-Loss以前先介绍一下Softmax
它是用来计算输出层每个神经元的几率的:
S o f t m a x = e y k ∑ e y k Softmax = \frac{e^{y_k}}{\sum{e^{y_k}}} Softmax=∑eykeyk
由于e的指数容易变得很是大,可能会致使溢出,因此对此函数进行了改进,以下:
S o f t m a x = e y k + C ∑ e y k + C Softmax = \frac{e^{y_k+C}}{\sum{e^{y_k+C}}} Softmax=∑eyk+Ceyk+C
这里的C能够是任何值,但为了防止溢出,通常使用输入信号的最大值,看一个具体的例子:
def softmax(x): if x.ndim == 2: x = x.T x = x - np.max(x, axis=0) y = np.exp(x) / np.sum(np.exp(x), axis=0) return y.T x = x - np.max(x) # 溢出对策 return np.exp(x) / np.sum(np.exp(x))
输入的X是一个数组,对他进行exp操做会返回一个与他大小相同,但对其中每个元素都进行exp操做的数组。
下面看Softmax-With-Loss的方向传播图
能够发现,反向传播以后返回值的是y-t,因此Softmax-With-Loss的代码以下:
class SoftmaxWithLoss: def __init__(self): self.loss = None self.y = None # softmax的输出 self.t = None # 监督数据 def forward(self, x, t): self.t = t self.y = softmax(x) self.loss = cross_entropy_error(self.y, self.t) return self.loss def backward(self, dout=1): batch_size = self.t.shape[0] if self.t.size == self.y.size: # 监督数据是one-hot-vector的状况 dx = (self.y - self.t) / batch_size else: dx = self.y.copy() dx[np.arange(batch_size), self.t] -= 1 dx = dx / batch_size return dx
全部须要用到的函数以及类都介绍完了,下面开始写Network
Network
class Network(object): def __init__(self, input_size, hide_size, output_size, weight_init_std=0.001): """ 用来声明该神经网络输入输出以及隐藏层的每一层层数 :param input_size: 输入层数 :param hide_size: 第一个隐藏层层数 :param output_size: 输出层层数 :param weight_init_std: 防止W过大 """ self.params = { 'w1': weight_init_std * np.random.randn(input_size, hide_size), 'b1': np.zeros(hide_size), 'w2': weight_init_std * np.random.randn(hide_size, output_size), 'b2': np.zeros(output_size),} self.layers = OrderedDict() # 使字典有序,能够记住像字典中添加元素的顺序 self.layers = { 'Affine1': Affine(self.params['w1'], self.params['b1']), 'Sigmoid': Sigmoid(), 'Affine2': Affine(self.params['w2'], self.params['b2'])} self.layer_last = SoftmaxWithLoss() def predict(self, x): """ 用来预测数据经过该模型后获得的结果 :param x: 预测的输入数据 :return: 返回该网路预测的结果 """ for layer in self.layers.values(): x = layer.forward(x) return softmax(x) def loss(self, x, t): """ 计算该神经网络所预测结果与实际结果的损失 其中y为预测结果 :param x: 输入数据 :param t: 真实结果 :return: 交叉熵损失 """ y = self.predict(x) return self.layer_last.forward(y, t) def accuracy(self, x, t): """ 识别计算精度 其中y为预测结果 :param x: 输入数据 :param t: 真实结果 :return: 正确率 """ y = self.predict(x) y = np.argmax(y, axis=1) if t.ndim != 1: t = np.argmax(t, axis=1) return np.sum(y == t) / float(x.shape[0]) def back_gradient(self, x, t): """ 进行反向传播算法 :return: 反向传播获得的偏导 """ self.loss(x, t) # 将涉及到的layer经过列表存储起来,以便于进行反向传播的遍历 layers = list(self.layers.values()) # 将layers倒序 layers.reverse() # 第一个反向传播的参数应该是一 dout = 1 # 反向传播通过输出层 dout = self.layer_last.backward(dout) # 反向传播从尾到头依次传播 for layer in layers: dout = layer.backward(dout) # 得到反向传播获得的各个参数的偏导 grads = { 'w1': self.layers['Affine1'].dW, 'b1': self.layers['Affine1'].db, 'w2': self.layers['Affine2'].dW, 'b2': self.layers['Affine2'].db } return grads
network须要介绍的都在注释中写的很清楚。
下面来开始训练:
# 读入数据 iris = load_iris() x_train, x_test, y_train, y_test = train_test_split(iris.data, iris.target) # x_train.shape = (112, 4), 因此input_size为4,hide_size能够根据本身的状况随意设置,output_size 觉得y有0, 1, 2三个元素,因此设置为3 network = Network.Network(input_size=4, hide_size=50, output_size=3) trying_time = 100000 # 循环的次数 train_size = x_train.shape[0] # 训练集的大小 batch_size = 1 # 每一次训练时挑选的一批数据的大小 learning_rate = 5e-4 # 学习率 min_loss = float('inf') # 起初min_loss为无穷大 # 每个epoch重复的次数 iter_per_epoch = max(train_size / batch_size, 1) # 开始训练 for i in range(trying_time): batch_mask = np.random.choice(train_size, batch_size) # 随机选择要进行训练的下标,因为choice是随机选择,因此大循环结束也不必定会对每个元素进行学习。 x_batch = x_train[batch_mask] y_batch = y_train[batch_mask] # 经过反向传播来求偏导,并记录在grad中 grad = network.back_gradient(x_batch, y_batch) # 更新各个参数 for key in ('w1', 'b1', 'w2', 'b2'): network.params[key] -= learning_rate * grad[key] # 计算损失 loss = network.loss(x_batch, y_batch) # 存放当前损失 if loss < min_loss: min_loss = loss if i % iter_per_epoch == 0: # 训练集的正确率 train_acc = network.accuracy(x_train, y_train) # 测试集的正确率X test_acc = network.accuracy(x_test, y_test) print(train_acc, test_acc) print('预测的结果为:{}'.format(np.argmax(network.predict(x_test), axis=1))) print('实际的结果为:{}'.format(y_test)) print('预测的正确率为:{}'.format(network.accuracy(x_test, y_test))) print('损失为:{}'.format(min_loss))
须要注意的是learning_rate的设置必定要合理,否则可能致使训练不会产生任何效果。
运行结果以下: