zip:
-r
recursion-d
delete-m
move (move隐藏的意思是,原文件会消失)想增加文件,不需要加参数- 什么参数也没有。如果zip压缩文件不存在,执行以上命令将会创建一个新的zip文件并将指定的文件添加进去。如果zip压缩文件已经存在,则新的文件将被追加到现有的zip文件中。
zip -r (recursively) SpikeZip.zip .\haq-master
内部函数,外部函数
如果不用nonlocal
关键字,内部函数将会创建新的局部变量 index
def outer_function():
index = 0
def inner_function():
nonlocal index
print("This is the inner function.")
print("This is the outer function.")
inner_function()
def another_function():
inner_function() # 这里会产生错误,因为 inner_function 不在该函数的作用域内
outer_function()
another_function() # 这里会产生错误,因为 inner_function 不在该函数的作用域内
vscode ctrl+shift+z反撤销
VGG模型
__init__
用来实例化vgg模型,总体架构分成五个layer用于特征提取和最后的classification用于分类。
每一个layer
是一个列表,放到sequential
容器里面。
这里说的layer是包含Con,BN,ReLU,Dropout
四部分的,作为一个整体。注意,其实只有Con是决定out_channel的,即我们所说的W,后面只是对这个W参数的一些操作。
cfg
中的数字代表经过每一层之后的特征向量的个数,即out_feature
,作为下一层的input channel
'VGG16': [
[64, 64, 'M'],
[128, 128, 'M'],
[256, 256, 256, 'M'],
[512, 512, 512, 'M'],
[512, 512, 512, 'M']
]
class VGG(nn.Module):
def __init__(self, vgg_name, num_classes, dropout):
super(VGG, self).__init__()
self.init_channels = 3
self.layer1 = self._make_layers(cfg[vgg_name][0], dropout)
self.layer2 = self._make_layers(cfg[vgg_name][1], dropout)
self.layer3 = self._make_layers(cfg[vgg_name][2], dropout)
self.layer4 = self._make_layers(cfg[vgg_name][3], dropout)
self.layer5 = self._make_layers(cfg[vgg_name][4], dropout)
if num_classes == 1000:
self.classifier = nn.Sequential(
nn.Flatten(),
nn.Linear(512*7*7, 4096),
nn.ReLU(inplace=True),
nn.Dropout(dropout),
nn.Linear(4096, 4096),
nn.ReLU(inplace=True),
nn.Dropout(dropout),
nn.Linear(4096, num_classes)
)
else:
self.classifier = nn.Sequential(
nn.Flatten(),
nn.Linear(512, 4096),
nn.ReLU(inplace=True),
nn.Dropout(dropout),
nn.Linear(4096, 4096),
nn.ReLU(inplace=True),
nn.Dropout(dropout),
nn.Linear(4096, num_classes)
)
for m in self.modules():
if isinstance(m, nn.Conv2d):
nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')
elif isinstance(m, nn.BatchNorm2d):
nn.init.constant_(m.weight, val=1)
nn.init.zeros_(m.bias)
elif isinstance(m, nn.Linear):
nn.init.zeros_(m.bias)
def _make_layers(self, cfg, dropout):
layers = []
for x in cfg:
if x == 'M':
layers.append(nn.MaxPool2d(kernel_size=2, stride=2))
else:
layers.append(nn.Conv2d(self.init_channels, x, kernel_size=3, padding=1))
layers.append(nn.BatchNorm2d(x))
layers.append(nn.ReLU(inplace=True))
layers.append(nn.Dropout(dropout))
self.init_channels = x
return nn.Sequential(*layers)
def forward(self, x):
out = self.layer1(x)
out = self.layer2(out)
out = self.layer3(out)
out = self.layer4(out)
out = self.layer5(out)
out = self.classifier(out)
return out
def vgg16(num_classes=10, dropout=0, **kargs):
return VGG('VGG16', num_classes, dropout)
什么是神经元
参考这个
但具体什么意思fanin&fanout的定义,根据实际问题来。不过多赘述,这个已经通过实践学会了。
nn.linear()和nn.Conv2d() & 数据流
numpy格式的输出:
首先你要明确,如果你在程序中打印它们的类型,那么没有任何区别。通过np.array()定义的 numpy 数组,只有一种类型:<class ‘numpy.ndarray’>。
但是你要是打印他们的 shape ,区别立刻显现.
nn.linear()
简单过一遍流程。官方教程写的太好了!直接去看就行!
定义:
torch.nn.Linear(in_features, out_features, bias=True, device=None, dtype=None)
self.weight = Parameter(torch.empty((out_features, in_features), **factory_kwargs))
下面是一个例子:重点在区分weight.shape & input,output.size & nn.Linear(in,out)不同维度代表的意思
《--------------------------------------------------------------------------------------------------------------------------------------------------------------------------》
m = nn.Linear(20, 30)
input = torch.randn(128, 20)
output = m(input)
print(output.size()) --> [128, 20] * [20, 30] ===> [128, 30]
print(m.weight.shape) --> [30, 20]!
in_features
in_features (int) – size of each input sample
指的是输入的二维张量的大小,即输入的[batch_size, size]
中的size
。
out_features
out_features (int) – size of each output sample
指的是输出的二维张量的大小,即输出的二维张量的形状为[batch_size,output_size]
,当然,它也代表了该全连接层的神经元个数。
从输入输出的张量的shape角度来理解,相当于一个输入为[batch_size, in_features]
的张量变换成了[batch_size, out_features]
的输出张量。
《--------------------------------------------------------------------------------------------------------------------------------------------------------------------------》
从源码吃透:
让我们来看看逻辑:
首先要有定义,实例化Class linear
。第一步:看init
:
init中定义了许多内部的属性,是由你给定的参数初始化的,这样你就可以知道其内部属性和你给的参数的关系,你可以用.访问。可以看到self.weight使用parameter生成的,证明weight是可学习的元素。同时可以发现其torchsize是(out_features, in_features)。
后面调用了类下的方法reset_parameters()后,完成初始化。
def __init__(self, in_features: int, out_features: int, bias: bool = True,
device=None, dtype=None) -> None:
factory_kwargs = {'device': device, 'dtype': dtype}
super().__init__()
self.in_features = in_features
self.out_features = out_features
self.weight = Parameter(torch.empty((out_features, in_features), **factory_kwargs))
if bias:
self.bias = Parameter(torch.empty(out_features, **factory_kwargs))
else:
self.register_parameter('bias', None)
self.reset_parameters()
实例化完成之后,我们还想知道需要传入和传出什么,这时候需要看的是forward方法实现。BTW,当今的forward不会需要你显式地调用。而是使用torch.nn.Sequential自动管理层的前向传播过程。
我们可以发现forward接受一个input参数,类型是Tensor,其返回值为Tensor。
可以看到他调用的是F中的Linear方法。
def forward(self, input: Tensor) -> Tensor:
return F.linear(input, self.weight, self.bias)
def extra_repr(self) -> str:
return 'in_features={}, out_features={}, bias={}'.format(
self.in_features, self.out_features, self.bias is not None
)
那我们最后看一下forward具体怎么实现的:注意注释中的内容。
可以解释weight为什么是(out_features, in_features)。在实际计算的时候是对W求转置,与input做矩阵乘。
一切不明皆在代码中!
nn.Conv2d
定义:torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True)
layer.conv.weight.shape:`[ C o u t C_{out} Cout, C i n C_{in} Cin, H H H, W W W] 注意和input和output参数区分,也要和定义nn.Conv2d传入的参数区分。
if transposed:
self.weight = Parameter(torch.empty((in_channels, out_channels // groups, *kernel_size), **factory_kwargs))
else:
self.weight = Parameter(torch.empty((out_channels, in_channels // groups, *kernel_size), **factory_kwargs))
if bias:
self.bias = Parameter(torch.empty(out_channels, **factory_kwargs))