El parámetro batchnorm2d torch_Pytorch es libre de cargar algunos parámetros del modelo y congelar

El método de carga Pytorch y el método load_ STATE_ dict solo pueden leer un archivo de parámetro relativamente fijo, requieren leer STATE_ dict la clave y Model.state_ dict () es igual a la clave correspondiente.

En el proceso de aprendizaje de la migración, es posible que solo necesitemos usar una parte de una red previamente entrenada, combinar múltiples redes en una red o separar el Sequential en el modelo de pre-entrenamiento para obtener el resultado de la capa intermedia, etc., estos casos. El método de carga tradicional no es muy efectivo.

Por ejemplo, si queremos usar las primeras 7 convoluciones de Mobilenet y congelar estas capas, la última parte está conectada a otra estructura, o reescrita en una estructura FCN, el método tradicional no funcionará.

El método más universal es: construir un diccionario para que las claves del diccionario sean las mismas que la red que creamos. Luego, completamos los parámetros deseados de varias redes pre-entrenadas a las nuevas claves para tener un nuevo state_ dict, entonces podemos cargar este nuevo estado _dict. En la actualidad, solo podemos pensar en este método para tratar con transformaciones de red más complejas.

En Internet, marque "cargar parte del modelo", "congelar parte del modelo" generalmente solo cambiar el FC, lo cual no es útil en absoluto. Cuando era un principiante, escribí el state_dict y pisé algunos pozos y envié para grabar.


1. Cargue algunos parámetros de preentrenamiento

Primero, echemos un vistazo a la estructura de Mobilenet.

(Fuente github, con modelo preentrenado mobilenet_sgd_rmsprop_69.526.tar)

class Net(nn.Module):

def __init__(self):

super(Net, self).__init__()


def conv_bn(inp, oup, stride):

return nn.Sequential(

nn.Conv2d(inp, oup, 3, stride, 1, bias=False),

nn.BatchNorm2d(oup),

nn.ReLU(inplace=True)

)


def conv_dw(inp, oup, stride):

return nn.Sequential(

nn.Conv2d(inp, inp, 3, stride, 1, groups=inp, bias=False),

nn.BatchNorm2d(inp),

nn.ReLU(inplace=True),


nn.Conv2d(inp, oup, 1, 1, 0, bias=False),

nn.BatchNorm2d(oup),

nn.ReLU(inplace=True),

)


self.model = nn.Sequential(

conv_bn( 3, 32, 2),

conv_dw( 32, 64, 1),

conv_dw( 64, 128, 2),

conv_dw(128, 128, 1),

conv_dw(128, 256, 2),

conv_dw(256, 256, 1),

conv_dw(256, 512, 2),

conv_dw(512, 512, 1),

conv_dw(512, 512, 1),

conv_dw(512, 512, 1),

conv_dw(512, 512, 1),

conv_dw(512, 512, 1),

conv_dw(512, 1024, 2),

conv_dw(1024, 1024, 1),

nn.AvgPool2d(7),

)

self.fc = nn.Linear(1024, 1000)


def forward(self, x):

x = self.model(x)

x = x.view(-1, 1024)

x = self.fc(x)

return x

Solo necesitamos las primeras 7 capas de convolución, y para facilitar la operación de concate en el futuro, desensamblamos el Secuencial y nos convertimos en los siguientes

class Net(nn.Module):

def __init__(self):

super(Net, self).__init__()


def conv_bn(inp, oup, stride):

return nn.Sequential(

nn.Conv2d(inp, oup, 3, stride, 1, bias=False),

nn.BatchNorm2d(oup),

nn.ReLU(inplace=True)

)


def conv_dw(inp, oup, stride):

return nn.Sequential(

nn.Conv2d(inp, inp, 3, stride, 1, groups=inp, bias=False),

nn.BatchNorm2d(inp),

nn.ReLU(inplace=True),


nn.Conv2d(inp, oup, 1, 1, 0, bias=False),

nn.BatchNorm2d(oup),

nn.ReLU(inplace=True),

)


self.conv1 = conv_bn( 3, 32, 2)

self.conv2 = conv_dw( 32, 64, 1)

self.conv3 = conv_dw( 64, 128, 2)

self.conv4 = conv_dw(128, 128, 1)

self.conv5 = conv_dw(128, 256, 2)

self.conv6 = conv_dw(256, 256, 1)

self.conv7 = conv_dw(256, 512, 2)


# 原来这些不要了

# 可以自己接后面的结构

'''

self.features = nn.Sequential(

conv_dw(512, 512, 1),

conv_dw(512, 512, 1),

conv_dw(512, 512, 1),

conv_dw(512, 512, 1),

conv_dw(512, 512, 1),

conv_dw(512, 1024, 2),

conv_dw(1024, 1024, 1),

nn.AvgPool2d(7),)


self.fc = nn.Linear(1024, 1000)

'''


def forward(self, x):

x1 = self.conv1(x)

x2 = self.conv2(x1)

x3 = self.conv3(x2)

x4 = self.conv4(x3)

x5 = self.conv5(x4)

x6 = self.conv6(x5)

x7 = self.conv7(x6)

#x8 = self.features(x7)

#out = self.fc

return (x1,x2,x3,x4,x4,x6,x7)

Creemos una red con una estructura modificada y veamos cuál es la diferencia entre su state_dict y el state_dict de nuestro archivo de preentrenamiento

net = Net()

#我的电脑没有GPU,他的参数是GPU训练的cudatensor,于是要下面这样转换一下

dict_trained = torch.load("mobilenet_sgd_rmsprop_69.526.tar",map_location=lambda storage, loc: storage)["state_dict"]

dict_new = net.state_dict().copy()


new_list = list (net.state_dict().keys() )

trained_list = list (dict_trained.keys() )

print("new_state_dict size: {} trained state_dict size: {}".format(len(new_list),len(trained_list)) )

print("New state_dict first 10th parameters names")

print(new_list[:10])

print("trained state_dict first 10th parameters names")

print(trained_list[:10])


print(type(dict_new))

print(type(dict_trained))

El resultado es el siguiente:

Después de que cortamos a la mitad, el parámetro cambió de 137 a 65. Los primeros diez parámetros muestran que el nombre ha cambiado pero el orden no ha cambiado. El tipo de datos de state_dict es Odict, que se puede operar de acuerdo con el método de operación de dict.

new_state_dict tamaño: 65 state_dict entrenado tamaño: 137
Nuevos nombres de los primeros 10 parámetros de
state_dict ['conv1.0.weight', 'conv1.1.weight', 'conv1.1.bias', 'conv1.1.running_mean', 'conv1 .1.running_var ',' conv2.0.weight ',' conv2.1.weight ',' conv2.1.bias ',' conv2.1.running_mean ',' conv2.1.running_var ']
entrenado state_dict first 10th nombres de parámetros
['module.model.0.0.weight', 'module.model.0.1.weight', 'module.model.0.1.bias', 'module.model.0.1.running_mean', 'module.model.0.1. running_var ',' module.model.1.0.weight ',' module.model.1.1.weight ',' module.model.1.1.bias ',' module.model.1.1.running_mean ',' module.model.1.1. running_var ' ]
<clase 'colecciones.OrderedDict'>
<clase 'colecciones.OrderedDict'>

Vemos que mientras construimos un diccionario para que las claves del diccionario sean las mismas que la red que creamos, podemos tener un nuevo state_dict completando los parámetros deseados de varias redes de pre-entrenamiento para las nuevas claves . de esta manera podemos cargar este nuevo estado _dict, que es el método más universal para todos los cambios de red.

for i in range(65):

dict_new[ new_list[i] ] = dict_trained[ trained_list[i] ]


net.load_state_dict(dict_new)

Hay otras situaciones. Por ejemplo, acabamos de agregar algunas capas en la parte posterior sin cambiar el nombre y la estructura de la capa de red original. Puede usar el siguiente método simple:

loaded_dict = {k: loaded_dict[k] for k, _ in model.state_dict()}

2. Congele estas capas de parámetros

Hay muchos métodos, y aquí se utiliza el método de congelación correspondiente al método anterior.

发现之前的冻结有问题,还是建议看一下

https://discuss.pytorch.org/t/how-the-pytorch-freeze-network-in-some-layers-only-the-rest-of-the-training/7088

或者

https://discuss.pytorch.org/t/correct-way-to-freeze-layers/26714

或者

En consecuencia, durante el entrenamiento, solo los parámetros de requirements_grad = True se pueden actualizar en el optimizador, por lo que

optimizer = torch.optim.Adam( filter(lambda p: p.requires_grad, net.parameters(),lr) )

 

 

Realice programación en Matlab, Python y C ++, aprendizaje automático, implementación y orientación de la teoría de la visión por computadora, tanto de pregrado como de maestría, comercio de pescado salado, respuestas profesionales, por favor, comuníquese con el número de QQ 757160542, si usted es el indicado

 

 

Supongo que te gusta

Origin blog.csdn.net/weixin_36670529/article/details/113903515
Recomendado
Clasificación