通过鸢尾花学习神经网络

   日期:2020-11-07     浏览:109    评论:0    
核心提示:[鸢尾花的种类]预测鸢尾花的种类Iris数据集在RA Fisher于1936年发表的经典论文《分类问题中的多重测量的使用》中使用,也可以在UCI中找到。它包括三个种类,每个种类有50个样本以及每朵花的一些特性。一种花可以与其他两种花是线性可分的,但是另两种不是线性可分的。如图目标分类预测鸢尾花的种类。进阶想办法在预测模型上提高精度说明给出导入数据的方式from sklearn.datasets import load_irisfrom sklearn.model_selecti

[鸢尾花的种类]

预测鸢尾花的种类

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

交叉熵误差

使用的是交叉熵误差公式为
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=N1nktnklogynk
对应的代码

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+ex1
在反向传播中他的导数为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 XL=YLWT
∂ L ∂ Y = X T ⋅ ∂ L ∂ Y \frac{\partial L}{\partial Y} = X^T·\frac{\partial L}{\partial Y} YL=XTYL
所以在反向传播中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返回反向传播的结果。(在两层的神经网络中他需要用到两次~)

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的设置一定要合理,不然可能导致训练不会产生任何效果。
运行结果如下:

 
打赏
 本文转载自:网络 
所有权利归属于原作者,如文章来源标示错误或侵犯了您的权利请联系微信13520258486
更多>最近资讯中心
更多>最新资讯中心
0相关评论

推荐图文
推荐资讯中心
点击排行
最新信息
新手指南
采购商服务
供应商服务
交易安全
关注我们
手机网站:
新浪微博:
微信关注:

13520258486

周一至周五 9:00-18:00
(其他时间联系在线客服)

24小时在线客服