minist手写数据集
随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习机器学习,本文就介绍了机器学习的基础内容。
机器学习分为监督学习,无监督学习和强化学习。
这三种方法经常被综合使用,让AI学会各种酷炫技能,比如最近兴起的一种训练方法GAN(生成对抗网络),它将两个AI放在一起,一个负责创作,一个负责找茬,通过来回对抗,最终训练一个创作达人,它可以无中生有,画出一张逼真的人脸,还能以假乱真搞艺术创作,画油画,写诗,作曲,都骗过了不少人,可以看到机器学习让AI越来越强大。
而深度学习就是监督学习。
NN: Neural Network(神经网络)
CNN: Convolutional Neural Networks)(卷积神经网络)
RNN: Recurrent Neural Networks (循环神经网路)
GNN:Graph Neural Network(图神经网络)
GAN:Generative Adversarial Network(生成对抗网络)
CNN使得NN从二维转化到三维,加速了训练
RNN引入了时间的概念,比方说,“我今天去打篮球”,已知“我”,推测后面一个词是“今天”,一般用来NLP(自然语言处理)。
GAN将两个AI放在一起,一个负责创作,一个负责找茬,通过来回对抗,最终训练一个创作达人,它可以无中生有,画出一张逼真的人脸
首先我们打开下面这个链接
神经网络模型
将num_neurons:1 改成1 后,点击simple data出现
把一个神经元比作切一刀,就相当于用一刀来切割图片的绿色和红色,而都改成2时,就会切两刀,会较好的分出红和绿的区分,如下:
现在我们把目光转向生物的神经元,神经元是怎样工作的呢,它接受一个电输入,输出一个电信号,看起来与机器一摸一样,这些机器也是接收一个输入,进行一些处理,然后弹出一个输出。
也许它的输出可能采取这样一种形式:输出 = (常数*输入)+(也许另一个常数)。
而另一方面,动物大脑表面上看起来以慢得多的节奏运行,却似乎以并行方式处理信号,模糊性是其计算的一种特征。
但观察表明,神经元不会立即反应,而是会抑制输入,直到输入增强,就像直到水装满了杯子,才可能溢出——神经元不希望传递微小的噪音信号,而只是传递有意识的明显信号。
s(sigmod)阈值函数: y = 1/1+e^-x
横坐标相当于是阈值,只有输入超过了阈值,才能产生输出信号。
总和输入值 x = a+b+c;
输出y s阈值函数 y = y(x)
有趣的是,如果只有其中一个输入大,其他输入小也能激发神经元,更重要的是,如果其中单个一般大,组合足够大,超过阈值,那么也能激发。即这些神经元也可以进行一些相对复杂,在某种意义上有点模糊的计算。
连接是神经元中最重要的东西。每一个连接上都有一个权重。
相当于两个神经元节点间的连接强度。
总和输入值x
x =a1*w1+a2*w2+a3*w3
S阈值输出
y = y(x) #y = 1/1+e^-x
如果是两个输入节点。我们可以这样看
x = (第一个节点的输出*链接权重)+(第二个节点的输出*链接权重)
于是我们可以将它看作两个矩阵相乘
(w1 w2)*(a1 = w1*a1 + w2*a2
a2)
以此类推,如果是两个输出
(w1,1 w2,1 * (a1 = (a1*w1,1 + a2*w2,1
w1,2 w2,2) a2) a1*w1,2 + a2*w2,2)
这样我们无需花费太多精力就可以实现神经网络了。
连接结构,我们把前后层每一个神经元与其他神经元互相连接,我们不采用创造性的方式将神经元连接起来,原来有两个:第一个是这种完全连接形式可以相对容易地编码成计算机指令,第二是神经网络的学习过程将会弱化这些实际上不需要的连接(也就是这些连接的权重将趋近于0)。零权重意味着这个链接实际上是断开了。
我们把中间层称为隐藏层,其实并没有多大高深的含义。
权重是怎么得来的呢,我们先随机初始化这些数字,由于大的初始权重会造成大的信
号传递给激活函数,导致网络饱和,因此我们应该避免大的初始权重值,我们可以
从-1.0~+1.0之间随机均匀的选择。
数学家得到的经验法则是:从均值为0,标准方差等于节点传入链接数量平方根倒数
的正太分布中进行采样。(使累加的权重范围变小)
0权重和相同的权重是要避免的。
(np.random.rand(self.hnodes, self.inodes)-0.5)#函数只能生成[0,1]的浮点数,于是 -0.5使范围变成[-0.5,0.5],(self.hnodes, self.inodes)为矩阵行,列
(np.random.normal(0.0,pow(self.hnodes,-0.5),(self.hnodes, self.inodes)#均值为0,标准方差等于节点传入链接数量平方根倒数的正太分布中进行采样
lambda 匿名函数
<函数名> = lambda<参数>:<表达式>
f = lambda x,y,z :x+y+z
至此我们可以写出的神经网络对象
class neturalNetwork :
# initialise the neural network
def __init__(self,inputnodes,hiddennodes,outputnodes,learningrate ):
# set numbers of nodes in each input ,hidden,output layer
self.inodes = inputnodes
self.hnodes = hiddennodes
self.onodes = outputnodes
# learning rate
self.lr =learningrate
# link weight matrices wih,who
# weights inside the arrays are w_i_j,where link is from node i to node j in the next layer
# w12 w21
# w12 w22 etc
# self.wih = (np.random.rand(self.hnodes, self.inodes) - 0.5)#随机初始化权重
# self.who = (np.random.rand(self.onodes, self.hnodes) - 0.5)
self.wih = (np.random.normal(0.0,pow(self.hnodes,-0.5),(self.hnodes, self.inodes)) )#较复杂的初始化权重
self.who = (np.random.normal(0.0,pow(self.onodes,-0.5),(self.onodes, self.hnodes)) )
# activation function is the sigmoid function
self.activation_function = lambda x: scipy.special.expit(x)#激活函数sigmod #使抑制输入 像生物一样
pass
根据以上,我们以3个输入层,3个隐藏层,3个输出层举例,
第一层 到 第二层
hidden_inputs = Wih * inputs#Wih 为input层到hidden层的权重矩阵
hidden_outputs = sigmod(hidden_inputs) #sigmod函数 加上阈值
第二层 到 第三层
final_inputs = Who * hidden_outputs#Who 为hidden层到output层的权重矩阵
final_outputs = sigmod(final_inputs)
由于输入的对象是以列的形式,所以我们要对输入的列表进行转置,并且矩阵拥有行和列,所以是2维的
inputs = np.array(input_list,ndmin=2).T#ndmin为定义数组最小维度 .T为转置
第一阶段,就是计算输出,所以我们正向查询的代码为
def query(self,input_list):
# convert inputs list to 2d array
inputs = np.array(input_list,ndmin=2).T#ndmin为定义数组最小维度 .T为转置
#calculate signals into hidden layer
hidden_inputs=np.dot(self.wih,inputs)
#calculate the signals emerging from hidden layer
hidden_outputs=self.activation_function(hidden_inputs)
#calculate signals into final layer
final_inputs =np.dot(self.who,hidden_outputs)
#calculate signals emerging from final layer
final_outputs = self.activation_function(final_inputs)
return final_outputs
pass
第二阶段 更新权重
首先我们得到误差,由目标矩阵减去最终矩阵
output_errors = targets -final_outputs
然后为隐藏层的误差构建矩阵,在输出层中,有两条路径对误差做出了“贡献”
分别为w1,1和w2,1,所以我们根据权重比例,对误差进行了分割,用e的一部分来更新w1,1:e*(w1,1/w,1,1+w2,1),类似的调整w2,1的e的部分为e*w2,1/(w1,1+w2,1),
这些分数的分母是一种归一化的因子,我们忽略这个因子,如果是两个输出节点
Error(hidden) = (w1,1 w1,2 *(e1 =(w1,1*e1+w1,2*e2
w2,1 w2,2) e2) w2,1*e1 +w2,2*e2)
这个权重矩阵和我们先前构建的矩阵很像,容易发现是进行了转置,
Error(hidden) =Whidden_output.T *Erroroutput
我们现在获得了最终层的误差和隐藏层的误差,现在我们来更新权重。但是这些节点都不是简单的线性分类,这些稍微复杂的节点,对加权后的信号进行求和,,并应用了S阈值函数,将所得到的输出给下一层节点。但这个表达式太复杂了,我们不能硬碰硬的求解这个表达式。
换一个思路,想象一下,在一个非常复杂,有波峰波谷的地形以及连绵的群山峻岭。在黑暗中伸手不见五指。你知道你是在一个山坡上,你需要坡底。对于整个地形,你没有精确的地图,只有一把手电筒,你只能使用手电筒,做近距离的观察,你可以看到某一块土地看起来是下坡,于是就小步往下走,缓慢下山。
在数学上,我们称之为梯度下降,首先我们画一个简单的函数y = (x-1)^2 +1,
用y表示误差,我们希望找到x,可以最小化y,我们随机在线上取一点,当斜率为正就
向左移,斜率为负就向右移,我们要将步子调小,避免超调,在最小值的地方来回反
弹。
这次我们用复杂的三维空间,同时用高表示函数的值,这次我们的山谷有多个,但并不都是最低的山谷,我们会卡在错误的山谷吗?答案是肯定的。
为了避免这种事发生,我们从山上的不同点开始,多次训练神经网络。
epoch =5#训练的次数
我们可以使用梯度下降法,计算出正确的权重吗?只要我们选择了合适的误差函数,这完全是可以的。
之前我们已得到误差,我们很容易把输出函数变成误差函数。
误差值 = (目标值-实际值)^2
要使用梯度下降,现在我们需要计算出误差函数相对于权重的斜率。也就是说我们想要知道“误差对链接权重的改变有多敏感”,这里我们会用到微分知识。
如果把网络连接权重(wi,j)为横坐标,神经网络误差(Error)为纵坐标,那么斜率就是
我们定义i,j,k,i为输入层,j为隐藏层,k为输出层
输出层输出ok,
节点误差=目标值 - 实际值 ek = tk -ok
我们反向传播,先看后两层,隐藏层和输出层
由微分的链式法则得
我们对上述式子各个击破,E = (tk-ok)^2,得到
ok是节点k的输出,如果你还记得,这是连续输入信号上进行加权求和,在得到的结果上应用函数得到的结果
由sigmod微分公式
带入,由于sigmod函数内部的表达式也需要对wj,k进行微分,因此我们对S函数微分项再次应用链式法则,这也非常容易,答案为oj,所以为
我们把2去掉,我们只关心斜率方向,ek = tk-ok
这就是我们所寻求的神奇表达式,也是训练神经网络的关键
以此类推输入层和隐藏层的误差函数斜率
更新后的权重wj,k是由刚刚得到的误差斜率取反来调整旧的权重而得到的
a为学习率,这个因子可以调节这些变化的长度,确保不会超调。
还是画出y = (x-1)^2+1,y为误差,x为权重
减号意味着,斜率为正就左移,减小误差
斜率为负就右移,减小误差
有点类似于牛顿迭代法,思想上是相同的
old-new = a *斜率
所以
表达式的最后一部分,是单行的水平矩阵,是之前输出的转置(之前输出是竖直的),因此通过这种形式可以让我们通过计算机编程语言高效的实现矩阵运算。
所以我们的反向训练代码为:
# train the netural network
def train(self,inputs_list,targets_list):
#convert inputs list to 2d array
inputs = np.array(inputs_list,ndmin=2).T
targets =np.array(targets_list,ndmin=2).T
# calculate signals into hidden layer
hidden_inputs = np.dot(self.wih, inputs)
# calculate the signals emerging from hidden layer
hidden_outputs = self.activation_function(hidden_inputs)
# calculate signals into final layer
final_inputs = np.dot(self.who, hidden_outputs)
# calculate signals emerging from final layer
final_outputs = self.activation_function(final_inputs)
# output layer error is the (target -actual)
output_errors = targets -final_outputs
# hidden layer error is the output_error,split by weights,recombined at hidden nodes
hidden_errors = np.dot(self.who.T,output_errors)
#update the weights for the links between the hidden and output layers
self.who +=self.lr * np.dot((output_errors * final_outputs *(1.0- final_outputs)),np.transpose(hidden_outputs))
#updata the weights for the links between the input and hidden layers
self.wih +=self.lr * np.dot((hidden_errors * hidden_outputs *(1.0-hidden_outputs)),np.transpose(inputs))
pass
minist手写数据集
这意味着纯文本中的每一个值都是由逗号分隔的
第一个值是标签,随后的值是手写数字的像素值,28*28 为784个
#load the minist training data csv file into a list
training_data_file = open ("minist_dataset/mnist_train.csv","r")
training_data_list = training_data_file.readlines()
training_data_file.close()
# number of input,hidden and output nodes
input_nodes = 784 #28*28
hidden_node = 200 #100#隐层在200以后不再变化
output_node = 10 #10分类
# learning rate is 0.5
learning_rate = 0.1#学习率在0.1左右表现最佳
# create instance of neural network
n =neturalNetwork(input_nodes,hidden_node,output_node,learning_rate)
#training the neural network
#epochs is the number of times the training data set is used for training
epochs =5#就是训练两遍
for e in range(epochs):#5 或 7 达到一个最佳点
# go through all records in the traing data set
for record in training_data_list:
#split the record by the ',' commas(逗号)
all_values = record.split(',')
#scale and shift(变换) the inputs
inputs = (np.asfarray(all_values[1:]) / 255.0 * 0.99)+0.01 #np.asfarray 将文本字符串转换为实数,并创建这些数字的数组
# 使当前范围为0.01到0.99
#create the target output values (all 0.01,except the desired label which is 0.99)
targets = np.zeros(output_node) + 0.01#创建一个10分类的目标 ,除正确的是0.99其他都是0.01
#all_value[0] is the target label for this record
targets[int(all_values[0])] = 0.99#列如0的目标是[0.99,0.01,0.01,...]
n.train(inputs,targets)
pass
创建一个列,答对了就写1,不对就写0
# test the neural network
scorecard =[]
for record in test_data_list:
all_values =record.split(',')
correct_label = int(all_values[0])
print(correct_label,"correct label")
inputs = (np.asfarray(all_values[1:]) /255 *0.99 +0.01)
outputs = n.query(inputs)
label = np.argmax(outputs)#np.argmax发现数组的最大值并返回它的位置
print(label,"network's answer")
if (label == correct_label):
scorecard.append(1)
else:
scorecard.append(0)
pass
pass
# calculate the performance score ,the fraction(一小部分) of correct answers
scorecard_array = np.asarray(scorecard)
print("performance=",scorecard_array.sum() / scorecard_array.size)