¿Cómo inicializo los pesos y sesgos de una red (a través de, por ejemplo, la inicialización de He o Xavier)?
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 en conv1.weight.data (que es un torch.Tensor ). Ejemplo:
conv1.weight.data.fill_(0.01)Lo mismo se aplica a los sesgos:
conv1.bias.data.fill_(0.01)nn.Sequential o custom nn.Module Pase una función de inicialización a torch.nn.Module.apply . Inicializará los pesos en todo el nn.Module recursivamente.
apply( fn ): aplica
fnrecursivamente a cada submódulo (tal como lo devuelve.children()), así como a sí mismo. El uso típico incluye inicializar los parámetros de un modelo (ver también torch-nn-init).
Ejemplo:
def init_weights(m): if isinstance(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)Si sigue el principio de la navaja 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 todos los pesos iguales, todas las neuronas en cada capa están produciendo el mismo resultado. 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) Validation Accuracy 9.625% -- All Zeros 10.050% -- All Ones Training Loss 2.304 -- All Zeros 1552.281 -- All OnesUna 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 and high=1.0 .
A continuación, veremos otra forma (además del código de la clase Net) de 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 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 del 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) Validation Accuracy 36.667% -- Uniform Weights Training Loss 3.208 -- Uniform WeightsLa regla general para establecer los pesos en una red neuronal es establecerlos para que estén cerca de cero sin que sean 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 uno cuyo peso se inicializa usando la regla general
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)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
Validation Accuracy 85.775% -- Uniform Rule [-y, y) 84.717% -- Normal Distribution Training Loss 0.329 -- Uniform Rule [-y, y) 0.443 -- Normal DistributionPara inicializar capas, normalmente no necesita hacer nada. PyTorch lo hará por ti. Si lo piensas bien, 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 la función de inicio de Kaiming He .
def reset_parameters(self): init.kaiming_uniform_(self.weight, a=math.sqrt(3)) 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, consulte aquí .
Para tener en cuenta: la ganancia de una inicialización adecuada es la velocidad de entrenamiento más rápida. Si su problema merece una inicialización especial, puede hacerlo después.