python - for - pytorch recurrent neural network
¿Cómo inicializar pesos en PyTorch? (7)
¿Cómo inicializar los pesos y sesgos (por ejemplo, con la inicialización He o Xavier) en una red en PyTorch?
Iterar sobre parámetros
Si no puede usar
apply
por ejemplo, si el modelo no implementa
Sequential
directamente:
Lo mismo para todos
# see UNet at https://github.com/milesial/Pytorch-UNet/tree/master/unet
def init_all(model, init_func, *params, **kwargs):
for p in model.parameters():
init_func(p, *params, **kwargs)
model = UNet(3, 10)
init_all(model, torch.nn.init.normal_, mean=0., std=1)
# or
init_all(model, torch.nn.init.constant_, 1.)
Dependiendo de la forma
def init_all(model, init_funcs):
for p in model.parameters():
init_func = init_funcs.get(len(p.shape), init_funcs["default"])
init_func(p)
model = UNet(3, 10)
init_funcs = {
1: lambda x: torch.nn.init.normal_(x, mean=0., std=1.), # can be bias
2: lambda x: torch.nn.init.xavier_normal_(x, gain=1.), # can be weight
3: lambda x: torch.nn.init.xavier_uniform_(x, gain=1.), # can be conv1D filter
4: lambda x: torch.nn.init.xavier_uniform_(x, gain=1.), # can be conv2D filter
"default": lambda x: torch.nn.init.constant(x, 1.), # everything else
}
init_all(model, init_funcs)
Puede probar con
torch.nn.init.constant_(x, len(x.shape))
para verificar que se hayan inicializado adecuadamente:
init_funcs = {
"default": lambda x: torch.nn.init.constant_(x, len(x.shape))
}
Para inicializar capas, normalmente no necesita hacer nada.
PyTorch lo hará por ti. Si lo piensas, esto tiene mucho sentido. ¿Por qué deberíamos inicializar capas, cuando PyTorch puede hacerlo siguiendo las últimas tendencias?
Compruebe, por ejemplo, la capa lineal .
En el método
__init__
llamará a
Kamming He
init function.
def reset_parameters(self):
init.kaiming_uniform_(self.weight, a=math.sqrt(5))
if self.bias is not None:
fan_in, _ = init._calculate_fan_in_and_fan_out(self.weight)
bound = 1 / math.sqrt(fan_in)
init.uniform_(self.bias, -bound, bound)
Lo mismo es para otros tipos de capas.
Para
conv2d
por ejemplo, marque
here
.
Para tener en cuenta: la ganancia de una inicialización adecuada es la mayor velocidad de entrenamiento. Si su problema merece una inicialización especial, puede hacerlo después.
Una sola capa
Para inicializar los pesos de una sola capa, use una función de
torch.nn.init
.
Por ejemplo:
conv1 = torch.nn.Conv2d(...)
torch.nn.init.xavier_uniform(conv1.weight)
Alternativamente, puede modificar los parámetros escribiendo a
conv1.weight.data
(que es un
torch.Tensor
).
Ejemplo:
conv1.weight.data.fill_(0.01)
Lo mismo se aplica a los prejuicios:
conv1.bias.data.fill_(0.01)
nn.Sequential
o personalizado
nn.Module
Pase una función de inicialización a
torch.nn.Module.apply
.
Inicializará los pesos en todo el
nn.Module
recursiva.
apply ( fn ): aplica
fn
recursiva a cada submódulo (como lo devuelve.children()
), así como a self. El uso típico incluye la inicialización de los parámetros de un modelo (ver también torch-nn-init).
Ejemplo:
def init_weights(m):
if type(m) == nn.Linear:
torch.nn.init.xavier_uniform(m.weight)
m.bias.data.fill_(0.01)
net = nn.Sequential(nn.Linear(2, 2), nn.Linear(2, 2))
net.apply(init_weights)
Comparamos diferentes modos de inicialización de peso usando la misma arquitectura de red neuronal (NN).
Todos ceros o unos
Si sigue el principio de la navaja de afeitar de Occam , podría pensar que establecer todos los pesos en 0 o 1 sería la mejor solución. Este no es el caso.
Con cada peso igual, todas las neuronas en cada capa están produciendo la misma salida. Esto hace que sea difícil decidir qué pesos ajustar.
# initialize two NN''s with 0 and 1 constant weights
model_0 = Net(constant_weight=0)
model_1 = Net(constant_weight=1)
- Después de 2 épocas:
Validation Accuracy
9.625% -- All Zeros
10.050% -- All Ones
Training Loss
2.304 -- All Zeros
1552.281 -- All Ones
Inicialización uniforme
Una distribución uniforme tiene la misma probabilidad de elegir cualquier número de un conjunto de números.
Veamos qué tan bien se entrena la red neuronal usando una inicialización de peso uniforme, donde
low=0.0
y
high=1.0
.
A continuación, veremos otra forma (además del código de clase Net) para inicializar los pesos de una red. Para definir pesos fuera de la definición del modelo, podemos:
- Defina una función que asigne pesos por el tipo de capa de red, luego
- Aplique esos pesos a un modelo inicializado usando
model.apply(fn)
, que aplica una función a cada capa de modelo.
# takes in a module and applies the specified weight initialization
def weights_init_uniform(m):
classname = m.__class__.__name__
# for every Linear layer in a model..
if classname.find(''Linear'') != -1:
# apply a uniform distribution to the weights and a bias=0
m.weight.data.uniform_(0.0, 1.0)
m.bias.data.fill_(0)
model_uniform = Net()
model_uniform.apply(weights_init_uniform)
- Después de 2 épocas:
Validation Accuracy
36.667% -- Uniform Weights
Training Loss
3.208 -- Uniform Weights
Regla general para establecer pesos
La regla general para establecer los pesos en una red neuronal es establecerlos para que estén cerca de cero sin ser demasiado pequeños.
Una buena práctica es comenzar sus pesos en el rango de [-y, y] donde
y=1/sqrt(n)
(n es el número de entradas a una neurona dada).
# takes in a module and applies the specified weight initialization
def weights_init_uniform_rule(m):
classname = m.__class__.__name__
# for every Linear layer in a model..
if classname.find(''Linear'') != -1:
# get the number of the inputs
n = m.in_features
y = 1.0/np.sqrt(n)
m.weight.data.uniform_(-y, y)
m.bias.data.fill_(0)
# create a new model with these weights
model_rule = Net()
model_rule.apply(weights_init_uniform_rule)
a continuación, comparamos el rendimiento de NN, pesos inicializados con distribución uniforme [-0.5,0.5) versus aquel cuyo peso se inicializa usando la regla general
- Después de 2 épocas:
Validation Accuracy
75.817% -- Centered Weights [-0.5, 0.5)
85.208% -- General Rule [-y, y)
Training Loss
0.705 -- Centered Weights [-0.5, 0.5)
0.469 -- General Rule [-y, y)
distribución normal para inicializar los pesos
La distribución normal debe tener una media de 0 y una desviación estándar de
y=1/sqrt(n)
, donde n es el número de entradas a NN
## takes in a module and applies the specified weight initialization
def weights_init_normal(m):
''''''Takes in a module and initializes all linear layers with weight
values taken from a normal distribution.''''''
classname = m.__class__.__name__
# for every Linear layer in a model
if classname.find(''Linear'') != -1:
y = m.in_features
# m.weight.data shoud be taken from a normal distribution
m.weight.data.normal_(0.0,1/np.sqrt(y))
# m.bias.data should be 0
m.bias.data.fill_(0)
a continuación mostramos el rendimiento de dos NN, uno inicializado con distribución uniforme y el otro con distribución normal
- Después de 2 épocas:
Validation Accuracy
85.775% -- Uniform Rule [-y, y)
84.717% -- Normal Distribution
Training Loss
0.329 -- Uniform Rule [-y, y)
0.443 -- Normal Distribution
Perdón por llegar tan tarde, espero que mi respuesta ayude.
Para inicializar pesos con una
normal distribution
use:
torch.nn.init.normal_(tensor, mean=0, std=1)
O para usar una
constant distribution
escriba:
torch.nn.init.constant_(tensor, value)
O para usar una
uniform distribution
:
torch.nn.init.uniform_(tensor, a=0, b=1) # a: lower_bound, b: upper_bound
Puede consultar otros métodos para inicializar tensores here
Si ve una advertencia de desaprobación (@ Fábio Perez) ...
def init_weights(m):
if type(m) == nn.Linear:
torch.nn.init.xavier_uniform_(m.weight)
m.bias.data.fill_(0.01)
net = nn.Sequential(nn.Linear(2, 2), nn.Linear(2, 2))
net.apply(init_weights)
import torch.nn as nn
# a simple network
rand_net = nn.Sequential(nn.Linear(in_features, h_size),
nn.BatchNorm1d(h_size),
nn.ReLU(),
nn.Linear(h_size, h_size),
nn.BatchNorm1d(h_size),
nn.ReLU(),
nn.Linear(h_size, 1),
nn.ReLU())
# initialization function, first checks the module type,
# then applies the desired changes to the weights
def init_normal(m):
if type(m) == nn.Linear:
nn.init.uniform_(m.weight)
# use the modules apply function to recursively apply the initialization
rand_net.apply(init_normal)