神经网络从经典到卷积

浪子不回头ぞ 提交于 2019-12-12 10:24:43

title: 神经网络从经典到卷积
date: 2019-07-21 14:48:14
categories:
- 深度学习
tags:
- 神经网络
- 卷积神经网络


神经网络是一种模拟人脑神经结构的计算机程序结构,以期能够实现人工智能的机器学习技术。本文将介绍神经网络背后的概念,并介绍如何通过编程实现神经网络。

神经网络基础

机器可以迅速做出大量的算术运算,但是无法处理图像中包含的大量信息。我们怀疑图像识别需要特别的人类智能, 而这是机器所缺乏的,人工智能所讨论的问题正是如此。

简单的学习器

一台基本的机器的工作流程,如下图所示,接受了一个问题并作出相应的思考,得到一个输出结果。

所有有用的计算机系统都应该有一个输入和输出,并在输入和输出之间进行某种类型的计算。我们不能精确知道一些事务如何运行,但我们可以通过模型来估计其运作方式。改进这些模型的方法是通过输出值和真实值之间的比较得到偏移值,进一步调整参数。

以一个简单的分类器为例子,有二维坐标内有两个点系,聚集在两片区域,先要用一条直线将这两类特征分开,即为通过判断在直线的那一端来对这两类进行分类。

我们设计一条经过原点的直线 y = A * x 作为我们分类的依据。这条直线应该基于具体的坐标数值特征将两组点分割开来。

为简化工作,将实例简化为以下的表格。

实例 x坐标 y坐标 类别
1 3.0 1.0 A
2 1.0 3.0 B

参数A控制着直线的斜率,我们给A赋予一个初值0.25,取x = 3.0, y = 0.75,我们所期望的直线应该经过A类别的上方,比如是1.1的目标值,我们可以利用这个误差来调整A值:

误差值 = 期望目标值 - 实际输出值
E = 1.1 - 0.75 = 0.35

分类器的线性函数应该修改为 t = (A + dA) * x

E = t - y = (A + dA) * x - A * x = dA * x
dA = E / x = 0.35 / 3 = 0.1167

这样A的新值化为A + dA =0.3667。 接下来可以接着输入B类的样本,使得A值进一步趋向于使直线略微低于B类点群的下方。但是这样的分类方法最终改进的直线不会顾及开始的训练值,而是仅仅考虑了最近的一组数据,这不是我们想要的结果。

机器学习中,我们应该进行适度改进(moderate),不应该改进过于激烈,当训练数据本身不能确定为完全正确,包含现实误差和噪声的时候,可以抑制这些影响因素。
在改进的公式中引入一个调节系数L,也是所谓的学习率(learning rate)

dA = L * (E / x)

选取L = 0.5 作为一个合理的系数,意味着我们只会更新值的一半。

一个简单的py实现样例代码如下。需要手工用文本写一个输入数据的文件classification cal sample.txt

import numpy as np
import matplotlib.pyplot as plt

datas = []  #初始化
file_object = open('classification cal sample.txt','r')
try:
    for line in file_object:
        # do_somthing_with(line) #line带"\n"
        line = line.replace(',',' ').replace('\n','')  #去除无用字符
        num = list(map(float,line.split()))  # 把空白行分离开,映射到表结构 参见python自带函数map() 
        datas.append(num) #后续拓展表
        #print(datas)
finally:
     file_object.close() #文件流关闭

plt.figure(num = 2019, figsize = (5, 5)) #初始化图形,指定figure的编号并指定figure的大小
for data in datas:
    a = data[0] # xi
    b = data[1] # yi
    k = b / a
    x = np.linspace(0, a, int(50 * a))
    y = k * x
    plt.plot(x,y,color = 'black', linewidth = 1.0, linestyle = '--') #定义线性

# y = A * x    E = dA * x     dA= L * (E / x)
A = 0.1
L = 0.5 #预设的初始值
# 进行简单的拟合  一个可循环的过程
for data in datas:
    E = data[1] - A * data[0] + data[1] / 10
    dA = L * (E / data[0])
    A = A + dA
    
x = np.linspace(0, 5, 250) #绘制图形
y = A * x
plt.plot(x,y,color = 'green', linewidth = 1.0)
plt.show()

有时候一个分类器不足以解决问题,如果数据本身不是单一线性过程支配,那么一个简单的线性分类器不能对数据进行划分。例如逻辑上的异或问题(如下方图)。这样的问题需要多个线性分类器来共同解决

神经元

大脑神经的基本单元是神经元,虽然神经元有着多种形式,神经元都会将电信号从一段传递到另一端,沿着突触,将电信号从树突传递到树突。神经元主要结构可以分为树突和终端。一个神经元接受了电信号,当接受信号超过一定阈值(threshold),神经元被激发后链接电路,产生一个输出信号。

思考我们如何建模人工神经。生物神经元可以接受很多输出,我们需要将他们加权相加得到总输入,通过内在的阈值函数计算得到输出结果,可以提供信号给更多的神经元。将这种结构复制到人造模型的一种方法是构造多层神经元,如图所示

每一层的节点都与所有下一层的节点通过一定的权重直接相连,通过调整节点之间的连接强度或阈值函数的形状来进行节点之间的强度调节

了解了处理结构之后,激活函数引入给我们的模型带入了非线性元素,可以处理更为复杂的问题,实际中较为常用的激活函数主要有三种,Sigmoid, tanh, 和ReLU

Sigmoid

(这里是因为我的Katex公式渲染还有一点问题,还得待调试,下面把python的计算代码贴上来)

y = 1 / (1 + np.exp(-x))

Sigmoid函数经过激活之后的输出区间为(0,1),输入的数据越大,输出越接近1,否则越接近0。sigmoid函数与生物神经网络的工作机制很类似,在一开始作为激活函数时广泛收到大众认可,但他的缺点也非常明显,他的最大问题在于会导致模型的梯度消失,函数的导数取值区间为0~0.25,在后向传播时,每逆向经过一个节点就要乘以一个sigmoid的导数值,当模型层次达到一定程度时,会导致梯度值越来越小,直到消失。

tanh

y1 = (np.exp(x) - np.exp(-x)) / (np.exp(x) + np.exp(-x))

tanh函数的输出结果是中心对称的,解决了激活函数在模型优化中收敛速度变慢的问题,然鹅求导的取值区域为0~1,仍然不够大

ReLU

f(x)= max(0,x)

ReLU修正线性单元 是现在在深度学习神经网络模型中使用率最高的激活函数,使用它在实际计算中非常高效,收敛速度非常快,但是函数特性可能会导致有一些神经元永远不会被激活,并且这些神经元参数不能被更新。这一般是由于模型参数在初始化的时候采用了全正或者全负的值,或者在后向传播中设置的学习率过高导致的。解决方法有对模型参数使用更高级的初始化方法,比如Xavier,以及合理的后向传播速率,推荐使用自适应的算法比如Adam

追踪信号

可以用矩阵的陈发表示所有的运算

X = W * I

W为权重矩阵,以第二层为视界,每一行为第一层的各个节点连接到第二层的某节点的权重序列;I矩阵为输入矩阵,X为组合调节之后的信号

O = function(X)

O表示着经过激活函数处理后的该层的所有输出

以一个结构为3x3的,有input、hidden、output层组成的神经网络作为算例,一次结果的输出过程如下:

I : input_data
Xhidden = Winput_hidden · I
Ohidden = sigmoid(Xhidden)
Xoutput = Whidden_output · Ohidden
Ooutput = Xoutput

后向传播

更新权重

python神经网络编程

py搭建简单的三层神经网络

import numpy as np
import scipy.special
#定义三层神经网络
class neuralNetwork:
    def __init__(self, inputnodes, hiddennodes, outputnodes, learningrate):
        self.inodes = inputnodes #输入层
        self.hnodes = hiddennodes #隐藏层
        self.onodes = outputnodes #输出层
        self.lr = learningrate
        #较为复杂的权重   中心值为0 标准差为传入节点数开方的倒数(-0.5次方) 的正态分布 
        self.wih = np.random.normal(0.0, pow(self.hnodes, -0.5), (self.hnodes, self.inodes))  #100 278
        self.who = np.random.normal(0.0, pow(self.onodes, -0.5), (self.onodes, self.hnodes))  #10 100

        self.activation_function = lambda x: scipy.special.expit(x)
        pass

    def train(self, inputs_list, targets_list):

        inputs = np.array(inputs_list, ndmin = 2).T  #278*1
        targets = np.array(targets_list, ndmin = 2).T # 10*1
        
        hidden_inputs = np.dot(self.wih, inputs) #100 1
        
        hidden_outputs = self.activation_function(hidden_inputs) #100 1

        final_inputs = np.dot(self.who, hidden_outputs) #10 1

        final_outputs = self.activation_function(final_inputs)  # 10 1

        output_errors = targets - final_outputs # 10 1

        hidden_errors = np.dot(self.who.T , output_errors) # 100 1
        #更新层之间的权重 dWij = alpha * Ek * S(Ok) * (1 - S(Ok)) * Oj.T
        self.who += self.lr * np.dot((output_errors * final_outputs * (1.0 - final_outputs)), np.transpose(hidden_outputs))
        self.wih += self.lr * np.dot((hidden_errors * hidden_outputs * (1.0 - hidden_outputs)), np.transpose(inputs))
        
        pass

    def query(self, inputs_list):

        inputs = np.array(inputs_list, ndmin = 2).T   
        hidden_inputs = np.dot(self.wih, inputs)
        hidden_outputs = self.activation_function(hidden_inputs)
        final_inputs = np.dot(self.who, hidden_outputs)
        final_outputs = self.activation_function(final_inputs)

        return final_outputs

#set number
input_nodes = 784
hidden_nodes = 100
output_nodes = 10

learning_rate = 0.3
#create instance of neural network
n = neuralNetwork(input_nodes, hidden_nodes, output_nodes, learning_rate)

#train the neural network
training_data_file = open("mnist_train.csv",'r')
training_data_list = training_data_file.readlines()
training_data_file.close()
i = 1
for record in training_data_list:
    all_values = record.split(',')
    inputs = (np.asfarray(all_values[1:]) / 255.0 * 0.99) + 0.01
    #print('正在训练第' + str(i) + '个')
    i += 1
    targets = np.zeros(output_nodes) + 0.01
    targets[int(all_values[0])] = 0.99   #1*10
    n.train(inputs, targets)
    pass
#print('训练结束\n')
#test the network

test_data_file = open("mnist_test.csv",'r')
test_data_list = test_data_file.readlines()
test_data_file.close()
score = 0
num = 0
for record in test_data_list:
    all_values = record.split(',')
##    image_aarray = np.asfarray(all_values[1:]).reshape((28,28))
##    plt.imshow(image_aarray, cmap = 'Greys', interpolation = 'None')
##    plt.show()
    correct_label = int(all_values[0])
    outputs = n.query((np.asfarray(all_values[1:]) / 255.0 * 0.99) + 0.01)
    label = np.argmax(outputs)
    #print('correct:' + str(correct_label) +' result:' + str(label))
    #下面是随便写的  凑合着看看
    if label == correct_label:
        score += 1
        num +=1
    else:
        num += 1
        pass
    pass
print('score:'+ str(score))
print('num:' + str(num))
print('rate:' + str(score/num))

深度学习框架下的神经网络编程

pytorch

Pytorch环境配置

env_torch配置

如果要安装可以在GPU上运行的版本,需要电脑配有NVDIA显卡,并且提前安装CUDA比较先进的版本
在anaconda的监视器界面中,新建一个包含适合pytorch工程的新环境,命名为env_tf,直接选择复制了tensorflow的环境然后把tf卸载掉
由于conda指令下载缓慢,选择pip下载,在环境prompt中分别输入

pip install https://download.pytorch.org/whl/cu100/torch-1.1.0-cp37-cp37m-win_amd64.whl
pip install https://download.pytorch.org/whl/cu100/torchvision-0.3.0-cp37-cp37m-win_amd64.whl

由于conda下载速度缓慢,建议使用加速器

jupyter notebook

该IDE环境使用较多,可在env_torch内通过下方指令安装,建议连加速器

conda install jupyter

tensorflow

卷积神经网络

卷积神经网络实战

以简单的mnist手写字符训练集为例子

tensorflow实现的简单卷积神经网络

from tensorflow.examples.tutorials.mnist import input_data
import tensorflow as tf

#加载原始数据
mnist = input_data.read_data_sets('C:/User/sheld/Desktop/data/', one_hot = True)
sess = tf.InteractiveSession()

#初始化权重和偏值函数  并加入噪声
def weight_variable(shape):
    initial = tf.truncated_normal(shape, stddev = 0.1)  #正态分布  噪声标准差设为0.1
    return tf.Variable(initial) 

def bias_variable(shape):
    initial = tf.constant(0.1, shape = shape) #正值 0.1    return tf.Variable(initial)
    return tf.Variable(initial)

#定义卷积层
#x:输入
#W:卷积参数 [a, b, c, d] a b 表示卷积核尺寸,c表示通道数, d表示卷积核数
#strides 步长
#padding 边界处理方式  same:输入和输出保持同样的尺寸
def conv2d(x, W):
    return tf.nn.conv2d(x, W, strides = [1, 1, 1, 1], padding = 'SAME')

#定义池化层
#2x2最大化池 缩小像素  把横竖步长设为2
def max_pool_2x2(x):
    return tf.nn.max_pool(x, ksize = [1,2,2,1], strides = [1,2,2,1],
                          padding = 'SAME')

#转化成2d结构  x是输入  y_是真实label
x = tf.placeholder(tf.float32, [None, 784]) #输入的数据占位符
y_ = tf.placeholder(tf.float32, [None, 10]) #输入的标签占位符
x_image = tf.reshape(x, [-1, 28, 28, 1])

#卷积层1
W_conv1 = weight_variable([5,5,1,32])
b_conv1 = bias_variable([32])
h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)
h_pool1 = max_pool_2x2(h_conv1)

#卷积层2
W_conv2 = weight_variable([5,5,32,64])
b_conv2 = bias_variable([64])
h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)
h_pool2 = max_pool_2x2(h_conv2)

#全连接层
W_fc1 = weight_variable([7 * 7 * 64, 1024])
b_fc1 = bias_variable([1024])
h_pool2_flat = tf.reshape(h_pool2, [-1, 7*7*64])
h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)

#Dropout层
keep_prob = tf.placeholder(tf.float32)
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)

#Softmax层
W_fc2 = weight_variable([1024, 10])
b_fc2 = bias_variable([10])
y_conv = tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)

#定义损失函数  应用Adam优化器
cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(y_conv),
                                              reduction_indices = [1])) #交叉熵
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy) #梯度下降法

#评定精准率
correct_prediction = tf.equal(tf.argmax(y_conv, 1), tf.argmax(y_, 1))  #精确度计算
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))

#开始训练
tf.global_variables_initializer().run()
for i in range(20000):
    batch = mnist.train.next_batch(50)
    if 1 % 100 ==0:
        train_accuracy = accuracy.eval(
            feed_dict = {x:batch[0], y_:batch[1], keep_prob: 1.0})
        print('step %d, training accuracy %g'%(i,train_accuracy))
        pass
    train_step.run(feed_dict = {x:batch[0], y_:batch[1], keep_prob: 0.5})
    pass
print('test accuracy %g' % accuracy.eval(
    feed_dict = {x:mnist.test.images, y_:mnist.test.labels, keep_prob: 1.0}))

pytorch实现的版本 我不知道放哪去了。。就不放出来了


易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!