什么是class,什么是instance,什么是object
Class:
可以理解为一个组装工厂。假如我们要生产一个机器人,那我们先要搭个工厂吧。先确定:我们要先安装胳膊,再安装头,我们的小破机器人的流水线就搭好了。这个工厂比较智能,胳膊和头的数量都可以调。
class BuildRobot():
def __init__(self,armcount,headcount):
self.armcount = armcount
self.headcount = headcount
所以这里的class,就是搭了一个工厂叫BuildRobot。‘init’ 就是告诉这个流水线,首先你需要这个机器人有几个胳膊(‘armcount’),有几个脑袋(‘headcount’)。先忽略一下这里的self,以后讲。
这个时候你可以run一下,这样你的class就搭好了。可是这时的工厂,因为你没有开始生产,是没有任何产出的。下面就是instance
instance:
可以理解为启动一次工厂生产出的机器人。现在我们用之前搭的工厂生产一个正常一点的机器人,两个胳膊一个脑袋:
normal_robot = BuildRobot(2,1)
查看一下胳膊数对不对?
normal_robot.armcount
2
我们再来一个 不太正常的机器人:
weird_robot = BuildRobot(4,1)
normal_robot 和weird_robot 都是instance。他们虽然胳膊数量不一样,但是本质上都是由这个class造出来的,由胳膊和头组成的机器人。
object:
大部分情况下,object和instance的含义是一样的,都是指这个造出来的robot。这两者的区别,只是在英语语言环境下的区别:
normal_robot is an instance of ‘buildrobot’
normal_robot is a ‘buildrobot’ object
上面这两个说法是等价的。
什么是method,什么是function?
两者都由def定义,稍微粗糙一点的理解就是,在class里面的function叫method。所以,method是和class,instance有关的一种function。
还是上面的工厂,我们现在加装一个车间,负责把胳膊上色:
class BuildRobot():
def __init__(self,armcount,headcount):
self.armcount = armcount
self.headcount = headcount
def paintarm(self,color):
print("paint arm:",color)
这个paintarm,就是一个method。还是一样,现在这个class没有生产,因此这个method也没有任何实际的产品出来。我们只能先生产出一个instance来:
colorful_robot = BuildRobot(2,1)
好的我们现在有一个两个胳膊一个头的robot了。这时,我们的robot还是没有上色的,因为我们没有让这个instance进入上色的那个车间。现在我们让这个robot进入车间,涂个红色。
colorful_robot.paintarm('red')
paint arm: red
上面的过程,就是call这个paintarm method。几个点:
如果没有先造一个机器人,这个车间就没有办法给胳膊上色,因此要上色,就必须先造一个机器人出来。所以,method是依赖于instance的。
这个车间只能给这个工厂产出的robot的胳膊上色,你从别的工厂拿一个车过来让他上色,他是不干的。因此,method是依赖于class的。只有这个class创建的instance,才能call这个method。
假如我把上色这个活,外包了。我就在外面另建了个工厂,专门上色,这就是function:
def outsourcing_paint(robot,color):
print("paint",robot,"arm:",color)
outsourcing_paint(colorful_robot,'red')
paint <__main__.BuildRobot object at 0x116b434a8> arm: red
这个外包的上色工厂,不管你这个东西是从哪个工厂来的,无论你是个机器人还是机器狗,反正我就拿来,给胳膊上色。
看到这里,应该就明白function和method的区别了。
注意method其实有两种,一种是instance method,一种是class method。
instance method就相当于对于机器人这个产品进行各种修改的车间。我给机器人上色,不影响我这个工厂的外形对吧?
class method,是对这个工厂,这个class的属性进行修改的车间,比如我有一个车间负责把工厂涂成红色的。这个行为并不影响我造出来的机器人的大小颜色属性。
本篇的讨论,我们先限定在instance method里。
SELF
把class, method讲明白以后,终于能讲self了。通过上面的例子,我们注意到
outsourcing_paint(colorful_robot,'red')
在function里面,是没有self的。因为我们告诉了外包工厂,给谁上色。所以在定义外包工厂function时,我们有两个input variables:robot 和 color。
colorful_robot.paintarm('red')
然鹅在使用method的时候,我们只告诉了车间,我要红色。那这个车间怎么知道,给哪个机器人上色啊?是给normal robot还是给colorful robot?因为我们在call这个method的时候,使用了colorful_robot.paintarm()这个格式,于是paintarm这个method就知道,哦,我要给这个colorful_robot上色。
在python里,要想使instance.method()这个格式可以正常工作,在class里面编写method的时候,就必须把变量的第一个位子留出来,用来指代未来call这个method的instance。就相当于我们在搭建给胳膊上色的这个车间的时候,就必须预留一个入口来放入已经生产出来的机器人。
留出来的这个位子,可以叫任何名字。只不过为了代码的优美,大部分人选择使用self,来指代使用这个method的instance他自己。
总结:
self是在为class编写instance method的时候,放在变量名第一个位子的占位词。
在具体编写instance method里,可以不使用self这个变量。
如果在method里面要改变instance的属性,可以用self.xxxx来指代这个属性进行修改。
所以self, 就是指由这个class造出来的instance嘛。
本小节转自知乎:https://zhuanlan.zhihu.com/p/95788606
错误与异常:
assert:断言
详细参见:
https://www.runoob.com/python3/python3-errors-execptions.html
PCA Jittering方法
PCA是主成分分析,它是为了减少数据集的维数,同时保持数据集中的对方差贡献最大的特征,保留低阶主成分,忽略高阶主成分。实施过程是对协方差矩阵进行特征分解,得到特征值和特征向量。 关于PCA,可以去查看链接。
根据AlexNet论文,内容描述为:
添加多个找到的主成分,其幅度与相应的特征值成正比,并要乘以一个零均值,标准差为0.1的高斯变量,对每一个RGB值 Ixy=[IxyR , IxyG , IxyB ] 要添加[p1 , p2 , p3] [α1λα1 , α2λα2 , α3λα3] ,p和λ是协方差矩阵的特征向量和特征值,α是高斯变量,该高斯变量的标准差参数也是唯一引入的外部变量
复现代码如下(使用方法在后面):
import numpy as np
import os
from PIL import Image, ImageOps
import random
from scipy import misc
import imageio
def PCA_Jittering(path):
img_list = os.listdir(path)
img_num = len(img_list)
for i in range(img_num):
img_path = os.path.join(path, img_list[i])
img = Image.open(img_path)
img = np.asanyarray(img, dtype = 'float32')
img = img / 255.0
img_size = img.size // 3 #转换为单通道
img1 = img.reshape(img_size, 3)
img1 = np.transpose(img1) #转置
img_cov = np.cov([img1[0], img1[1], img1[2]]) #协方差矩阵
lamda, p = np.linalg.eig(img_cov) #得到上述协方差矩阵的特征向量和特征值
#p是协方差矩阵的特征向量
p = np.transpose(p) #转置回去
#生成高斯随机数********可以修改
alpha1 = random.gauss(0,3)
alpha2 = random.gauss(0,3)
alpha3 = random.gauss(0,3)
#lamda是协方差矩阵的特征值
v = np.transpose((alpha1*lamda[0], alpha2*lamda[1], alpha3*lamda[2])) #转置
#得到主成分
add_num = np.dot(p,v)
#在原图像的基础上加上主成分
img2 = np.array([img[:,:,0]+add_num[0], img[:,:,1]+add_num[1], img[:,:,2]+add_num[2]])
#现在是BGR,要转成RBG再进行保存
img2 = np.swapaxes(img2,0,2)
img2 = np.swapaxes(img2,0,1)
save_name = 'pre'+str(i)+'.png'
save_path = os.path.join(path, save_name)
misc.imsave(save_path,img2)
#plt.imshow(img2)
#plt.show()
PCA_Jittering('testpic')
使用方法是:主函数PCA_Jitterring括号中的参数是当前路径下的一个文件夹,它会自动加载文件夹中的图片。调整该方法的唯一参数(高斯函数的标准差)是在标记了生成高斯随机数那里,改变参数会得到很不同的效果。
Dropout
视频学习:https://morvanzhou.github.io/tutorials/machine-learning/torch/5-03-dropout/
文章转载:https://blog.csdn.net/u012762419/article/details/79534085
1.dropout解决的问题
深度神经网络的训练是一件非常困难的事,涉及到很多因素,比如损失函数的非凸性导致的局部最优值、计算过程中的数值稳定性、训练过程中的过拟合等。其中,过拟合是很容易发生的现象,也是在训练DNN中必须要解决的问题。
过拟合
我们先来讲一下什么是“过拟合”。过拟合是指模型训练到一定程度后,在训练集上得到的测试误差远大于在测试集上得到的误差,如下图所示:
过拟合效果示意图
导致过拟合的直接原因是模型学习了太多噪声模式,并错误的将其视为数据模式;导致过拟合的主要原因有:
- 训练数据集太小
- 模型太复杂
- 过度训练
常用的过拟合解决方案
知道了产生过拟合的原因,就可以制定相应的解决方案,一般而言,解决的主要方法有:增加训练数据量、减少模型的复杂度、添加正则项等。在深度学习中,以上方法都可以使用,但是dropout是一个更加高效、简单的防止过拟合的方法。
2.dropout
dropout是指在训练一个大的神经网络的时候,随机“关闭”一些神经元,即把这些神经元从网络中“抹去”,这相当于在本次训练中,这些被“抹去”的神经元不参与本次训练,英文即是“dropout”的意思。如下图所示:
dropout示意图
我们看到图中打叉的神经元就是被“dropout”掉的神经元,和这些个神经元相连接的权重值也一并被“抹去”,不参与本次训练。不参与本次训练是说在当前的batch中,不参与训练,每个batch都会随机挑选神经元做dropout。
dropout为什么可以防止过拟合
我们知道“随机森林”是一种不容易发生过拟合的算法,其采用的方法是“bagging”,即通过对多个树的输出结果加权平均,得出最后的结果。而每棵树都是在不同的训练集上、设置不同的参数得到的,因此是一种典型的通过多个不同参数,甚至不同类型的模型来降低方差的一种方法。这种方法对传统的机器学习算法、或者小型的NN模型可行,但是当数据量很大、模型很复杂的时候,我们不能训练多个不同的模型出来做”bagging”,因为深度神经网络的训练是一个很耗时的过程,需要大量的计算资源和时间。
dropout则为这种思想提供了一种“廉价”的解决方案,因为每一次迭代的过程中,我们会随机dropout掉一些神经元(至于在那一层做dropout,需要看不同的情况),如果设置的dropout的值为0.5,则表示每个神经元有50%的概率被留下来,50%的概率被”抹去“。这就相当于我们从原来的神经网络中随机采样了50%的节点,组成了一个新的神经网络,这个是原来的神经网络的一个子网络,但是规模要比原来的神经网络小很多,并且训练代价也比较小, 这个新的子网络就相当于是”随即森林“中的一个子树(sub-tree)了。我们多次迭代优化,每次迭代优化都会做这样的”随机采样“,从原来的网络中构造一个子网络(sub-network),而每次构造的网络也都不尽相同,这就使得学习到的sub-network在结果上差异性,最后再对这些每个迭代学习到的不同的sub-network做一个”bagging“来得到最终的输出结果。
dropout中的一些细节问题
如何对多个不同的sub-network做bagging:这里并没有做真正意义上的bagging,我们预测的时候,其实使用的整个完整的神经网络,并没有做任何dropout。这里面有一个”权值共享“的概念,正是这个概念让dropout的计算变得可行。因为每次迭代都会产生不用的sub-network,优化不同的权值,这必然使得前后两次之间的sub-network有相同的神经元被保留下来,那么这些神经元对应的权值没有必要在重新开始优化,而是继续沿用上一个迭代优化好的值。这就大大减少了优化单个sub-network的时间,使得在很少的时间内,这个sub-network就可以得到不错的泛化效果。否则,每个迭代步骤都要从新初始化权值矩阵,开始优化,这又是一个巨大的计算开销。
train 和 test 的时候,dropout的概率怎么设置:按照原始的论文中,假设dropout的值是 p%,原始神经网络的神经元个数是N,因为在训练的过程中只有 p% 的神经元被保留下来,相应也只有p%的需要被优化的权值保留下来,这导致dropout后sub-network的输出也是整个原始神经网络的输出值的p%。所以,在测试的是时候使用的整个神经网络,我们只需要将每一层的权值矩阵乘以p%就可以保证测试网络的输出期望和训练网络的输出期望值大小一致了。
train and test dropout set
注意,如果你使用了tensorflow,则在测试的时候要保持dropout的值为1,即不”抹去“任何神经元,这个和具体的实现有关,感兴趣的可以看看tensorflow官网关于dropout函数的API。
为什么很少见CNN层加dropout: 这种情况确实不多见,典型的TextCNN模型,就是没有在卷积层加dropout。但是原始论文中确实又指出可以在卷积层做dropout ,只是收益并不是太明显。另外,dropout对于具有大量参数的全连接效果最好,而CNN的卷积层不是全连接,参数不是很多,所以效果不明显。论文还建议如果在CNN中加,最好是在开始的层加dropout,越往后的层,越是要小心加dropout。
神经网络加上dropout后,test loss 比 train loss还要小:正常,在不考虑测试集采样偏差的情况下,这种情况的解释是:每次train loss是在一个batch上计算的,而单个batch又是在一个通过dropout得到的sub-network计算得到的,即相当于在单颗树上得到的train loss;而测试的时候,用的整个神经网络,即相当于在整个”森林“上做预测,结果当然会好一下。