Python从零开始(库的安装与初步使用3+习题1.6)

来源:互联网 发布:类似知乎的软件有哪些 编辑:程序博客网 时间:2024/06/03 22:44

前言:

这个MarkDown编辑器真纠结,我以为上面的保存到线上草稿箱就是保存到我的草稿箱,结果MarkDown的草稿箱就只有一个,相当于缓存,会自动覆盖上一个,也就是说不能同时进行两个笔记。之前写了好多都没了,又得从新写。

正文:
上次的问题解决了,它说wheel pyyaml出了错,我自己去http://www.lfd.uci.edu/~gohlke/pythonlibs/下一个pyyaml装上就好了(这个连接要复制粘贴到地址框,不能从csdn转过去,{额外问题1}):
这里写图片描述

我查了一下,pyyaml是用于Python的Yaml(一种语言)解析器和发射器(emitter 是什么意思?{问题1})。wheel是用来代替egg的压缩包。不过为什么它需要执行这一部而不是直接安装呢{问题2}?

之后做《神经。。。》那本书上的1.5实验,具体细节我在阅读笔记上说明。

首先是样本数据生成,我计划训练样本和测试样本各生成2个文件,一个数据一个标签:

import randomimport math#打开文件training_sample=open("training_sample.txt",'w')training_label=open("training_label.txt",'w')test_sample=open("test_sample.txt",'w')test_label=open("test_label.txt",'w')#下面是样本数据生成算法#三个参数r=10w=6d=0#进入循环i=1while  i<3000+1:    #在方形空间中随机生成一点    x=random.uniform(-(w/2)-r,2*r+(w/2))    y=random.uniform(-(w/2)-r-d,r+(w/2))    #如果此点在任一半月形区域内,则写入相应文件,i加1,否则抛弃并从新生成    if (r-(w/2))**2<=x**2+y**2<=(r+(w/2))**2 and y>0:        if i>1000:             test_sample.write(str(x)+' '+str(y)+'\n')            test_label.write('1\n')        else:            training_sample.write(str(x)+' '+str(y)+'\n')            training_label.write('1\n')        i=i+1    else:         if (r-(w/2))**2<=(x-r)**2+(y+d)**2<=(r+(w/2))**2 and y<-d:            if i>1000:                 test_sample.write(str(x)+' '+str(y)+'\n')                test_label.write('-1\n')            else:                training_sample.write(str(x)+' '+str(y)+'\n')                training_label.write('-1\n')            i=i+1  #保存并关闭文件training_sample.close()training_label.close()test_sample.close()test_label.close()

这个过程我发现了Python的一些特点。
一,缩进也是语法,这和C++就不一样了,C++用{}来分块,而Python则用缩进分块。参考了http://www.cnblogs.com/zxf330301/articles/5415750.html
二,逗号的运用,详见http://www.cnblogs.com/wzjbg/p/6211957.html

另外,我试着用了C++里的文件操作,竟然成功了,看来Python的文件操作几乎和C++一样,这里的文件打开模式为写“w”。写入字符串时先用str()把数值转换成字符串,官网又上不去了,所以我就参考了http://www.cnblogs.com/Joseph-AMI/p/4713003.html

字符串中空格代表向量的下一维,换行代表下一向量。

Python的运算符优先级:

运算符 描述
lambda Lambda表达式
or 布尔“或”
and 布尔“与”
not x 布尔“非”
in,not in 成员测试
is,is not 同一性测试
<,<=,>,>=,!=,== 比较
| 按位或
^ 按位异或
& 按位与
<<,>> 移位
+,- 加法与减法
*,/,% 乘法、除法与取余
+x,-x 正负号
~x 按位翻转
** 指数
x.attribute 属性参考
x[index] 下标
x[index:index] 寻址段
f(arguments…) 函数调用
(experession,…) 绑定或元组显示
[expression,…] 列表显示
{key:datum,…} 字典显示
‘expression,…’ 字符串转换

以上来自https://www.cnblogs.com/xiehui/p/4146690.html,想看工整排版的可以点。

还有,Python的单引号’和双引号”扩起来的都表示无结尾符的字符串,不像C,C里单引号扩起来的是单个字符,双引号扩起来的是有结尾符的字符串。具体参考http://blog.csdn.net/wanghai__/article/details/6285310

之后检测一下数据:

import matplotlib.pyplot as pltimport numpy as nptraining_sample=np.loadtxt("training_sample.txt")plt.scatter(training_sample[:,0],training_sample[:,1])plt.show()

matplotlib是一个画图库,需要安装一下,scatter用来画散点图,其中有两个必要参数,分别是坐标x和y的数组。另外关于numpy的元素操作可参考http://blog.csdn.net/Savinger/article/details/52880078

结果显示数据正常:
这里写图片描述

接下来是建模训练:

import numpy as npfrom keras.models import Sequentialfrom keras.layers import Densefrom keras.optimizers import SGDfrom keras import backend, metrics#创建了新类,继承SGDclass SGD_c(SGD):    #添加了一个参数linear_decay    def __init__(self, lr=0.01, momentum=0., decay=0.,linear_decay=0.,                 nesterov=False, **kwargs):        self.linear_decay = linear_decay        super(SGD_c, self).__init__(lr, momentum, decay,                 nesterov, **kwargs)    #先处理线性衰减,之后进入父类函数    def get_updates(self, loss, params):        lr_t = self.lr        if self.linear_decay > 0:            self.lr -= self.linear_decay * backend.get_value(self.iterations)        self.updates = super(SGD_c, self).get_updates(loss, params)        self.lr = lr_t        return  self.updates#读入数据training_sample=np.loadtxt("training_sample.txt")training_label=np.loadtxt("training_label.txt",'int')test_sample=np.loadtxt("test_sample.txt")test_label=np.loadtxt("test_label.txt",'int')#建立模型model=Sequential()#添加一层全连接层,一个节点,输入向量的维度为2,激活函数为tanhmodel.add(Dense(1, input_dim=2, activation='tanh'))#损失就是书中的MSE,优化器为自己改的SGD,其参数为初始学习率和和线性衰减model.compile(loss='mean_squared_error', optimizer=SGD_c(lr=0.1, linear_decay=(0.1-0.00001)/50), metrics=[metrics.binary_accuracy])#model.get_layer(index=1).set_weights([np.array([[0],[1]]), np.array([0])])#训练模型,反复50次model.fit(training_sample, training_label, epochs=50, batch_size=1000)#评估模型,打印评估结果print(model.evaluate(test_sample, test_label, batch_size=2000))#打印决策边界print(model.get_layer(index=1).get_weights())

结果如下:
前几次迭代:
这里写图片描述

最后几次迭代,加上评估结果与决策边界:
这里写图片描述
(最后的决策边界为:-0.01782304x+1.84150457y+0.00046315=0,接近x轴)

我注意到个小细节,loadtxt会把[[a],[b],[c]….]1*n这种数据读成一维的数据串。我在官方文档看到了其dtype 参数的介绍:
dtype : data-type, optional
Data-type of the resulting array; default: float. If this is a structured data-type, the resulting array will be 1-dimensional, and each row will be interpreted as an element of the array. In this case, the number of columns used must match the number of fields in the data-type.
上面说当读入的是结构化数据时,结果会转换成一维数据,各行会变为一维数组的各元素。

我在Keras官网文档看了一圈,没找到阈值激活函数,我就先用tanh代替了,之前的题目也证明了其效果。
并且也没有书中的损失函数:
这里写图片描述
Keras里所有可用loss的参数都是真实分类结果与预测分类结果。没办法,就用均方差代替吧。

优化器中也没有书中所说的批梯度下降,但是有一个随机梯度下降(SGD),我在https://www.cnblogs.com/eniac1946/p/7423865.html中大概了解了一下,发现也可以用来代替,但是要把batch_size设置成总样本数。

另外关于decay的具体作用,我搜到了https://stats.stackexchange.com/questions/211334/keras-how-does-sgd-learning-rate-decay-work,上面说可以直接查看源码,每次更新学习率如下:

    def get_updates(self, loss, params):        grads = self.get_gradients(loss, params)        self.updates = [K.update_add(self.iterations, 1)]        lr = self.lr        if self.initial_decay > 0:            lr *= (1. / (1. + self.decay * K.cast(self.iterations, K.dtype(self.decay))))

cast的作用是把iterations转换成decay的格式。可以看到在第一次计算学习率时衰减已经计算在内了。以每次学习的学习率为中介,线性衰减值的计算如下:
这里写图片描述
其中,i为当前学习的次数(即当前是第i次迭代),ηi为第i次学习的学习率,m为最大学习次数(50),d为衰减值。转换后得:
这里写图片描述

但是如果要修改代码的话就不需要这样再转换一下,就像程序里的一样。
我看了源码,用于计算和返回的是函数里的局部lr,所以我就干脆先把self.lr改了,等它计算完再改回来。

关于继承,可以用super或直接self.parent。我参考了https://www.cnblogs.com/wjx1/p/5084980.html,总结一下就是用super 的话可以保证其每个父类只运行一次。

结语:

有几点还是没明白:
1,官方文档说evaluate()的batch_size默认是32,可是评估要这个是做什么用的?{问题3}

2,用vs调试Python代码时,调试到断点后各种变量的值我看不懂,因为本次已经耽误了很久了,所以我也没有搞的很明白,变量都是用print等函数打印出来看的。Python变量的储存方法还是要研究研究。{问题4}

3,可以看到我在代码中一段注释掉的代码:

model.get_layer(index=1).set_weights([np.array([[0],[1]]), np.array([0])])

这行代码是用来设置初始权值的,可以把初始的决策边界设置成y=0(即x轴)。因为我之前生成数据时没有生成边界数据,所以如果这样设置了,理论上应该是分类正确率应该是1,但为什么总差一点呢?{问题5}而且一共有2000个测试样本,理论上正确率应该是0.0005的整数倍,而不是像上面那样这么多小数位。

4,如上,我把初始的决策边界设置成y=0,把标签改成1和0,并且激活函数改用sigmoid,之后正确率就正常地为1了。改了标签之后,sigmoid和tanh不应该是一样的吗,为什么有区别?{问题6}

终于告一段落了,接下来就要好好琢磨《神经网络。。。》的第二章了。

欢迎回答我留下的问题。不过如果有问题也欢迎来向我提问。

原创粉丝点击