Cómo hacer un bot que convierta una foto en un cómic. Tercera parte Modelo de alojamiento gratuito sin servidor + GPU

⇨ Parte 1
⇨ Parte 2


Bueno, descansado y eso es suficiente. Bienvenido de nuevo!


En la serie anterior, usted y yo recopilamos datos y formamos a nuestro primer modelo.
Luego, horrorizados por los resultados, entrenaron a una docena más.
¡Es hora de mostrar nuestra creación al mundo!


Modelo de exportación


Para comenzar, volveremos a guardar el modelo del generador en un formato adecuado para que no tengamos que arrastrar las declaraciones de clase al alojamiento.


Cree un pequeño archivo con la extensión * .py y copie el código desde debajo del spoiler a continuación.
Deja que sea jit.py:


El código que se copiará sin pensar reemplazando path, output_path con el suyo
#  path       #    *G_A.pth -   ->  # output_path -      #      *.jit,  -  ,     path= '/checkpoints/resnet9_nowd_nodo_128to400_c8/60_net_G_A.pth' output_path ='/checkpoints/resnet9_nowd_nodo_128to400_c8/resnet9_nowd_nodo_128to400_c8_160-50-60_1.jit' import torch from torch import nn class ResnetGenerator(nn.Module): """Resnet-based generator that consists of Resnet blocks between a few downsampling/upsampling operations. We adapt Torch code and idea from Justin Johnson's neural style transfer project(https://github.com/jcjohnson/fast-neural-style) """ def __init__(self, input_nc, output_nc, ngf=64, norm_layer=nn.BatchNorm2d, use_dropout=False, n_blocks=6, padding_type='reflect'): """Construct a Resnet-based generator Parameters: input_nc (int) -- the number of channels in input images output_nc (int) -- the number of channels in output images ngf (int) -- the number of filters in the last conv layer norm_layer -- normalization layer use_dropout (bool) -- if use dropout layers n_blocks (int) -- the number of ResNet blocks padding_type (str) -- the name of padding layer in conv layers: reflect | replicate | zero """ assert(n_blocks >= 0) super(ResnetGenerator, self).__init__() if type(norm_layer) == functools.partial: use_bias = norm_layer.func == nn.InstanceNorm2d else: use_bias = norm_layer == nn.InstanceNorm2d model = [nn.ReflectionPad2d(3), nn.Conv2d(input_nc, ngf, kernel_size=7, padding=0, bias=use_bias), norm_layer(ngf), nn.ReLU(True)] n_downsampling = 2 for i in range(n_downsampling): # add downsampling layers mult = 2 ** i model += [nn.Conv2d(ngf * mult, ngf * mult * 2, kernel_size=3, stride=2, padding=1, bias=use_bias), norm_layer(ngf * mult * 2), nn.ReLU(True)] mult = 2 ** n_downsampling for i in range(n_blocks): # add ResNet blocks model += [ResnetBlock(ngf * mult, padding_type=padding_type, norm_layer=norm_layer, use_dropout=use_dropout, use_bias=use_bias)] for i in range(n_downsampling): # add upsampling layers mult = 2 ** (n_downsampling - i) model += [nn.ConvTranspose2d(ngf * mult, int(ngf * mult / 2), kernel_size=3, stride=2, padding=1, output_padding=1, bias=use_bias), norm_layer(int(ngf * mult / 2)), nn.ReLU(True)] model += [nn.ReflectionPad2d(3)] model += [nn.Conv2d(ngf, output_nc, kernel_size=7, padding=0)] model += [nn.Tanh()] self.model = nn.Sequential(*model) def forward(self, input): """Standard forward""" return self.model(input) class ResnetBlock(nn.Module): """Define a Resnet block""" def __init__(self, dim, padding_type, norm_layer, use_dropout, use_bias): """Initialize the Resnet block A resnet block is a conv block with skip connections We construct a conv block with build_conv_block function, and implement skip connections in <forward> function. Original Resnet paper: https://arxiv.org/pdf/1512.03385.pdf """ super(ResnetBlock, self).__init__() self.conv_block = self.build_conv_block(dim, padding_type, norm_layer, use_dropout, use_bias) def build_conv_block(self, dim, padding_type, norm_layer, use_dropout, use_bias): """Construct a convolutional block. Parameters: dim (int) -- the number of channels in the conv layer. padding_type (str) -- the name of padding layer: reflect | replicate | zero norm_layer -- normalization layer use_dropout (bool) -- if use dropout layers. use_bias (bool) -- if the conv layer uses bias or not Returns a conv block (with a conv layer, a normalization layer, and a non-linearity layer (ReLU)) """ conv_block = [] p = 0 if padding_type == 'reflect': conv_block += [nn.ReflectionPad2d(1)] elif padding_type == 'replicate': conv_block += [nn.ReplicationPad2d(1)] elif padding_type == 'zero': p = 1 else: raise NotImplementedError('padding [%s] is not implemented' % padding_type) conv_block += [nn.Conv2d(dim, dim, kernel_size=3, padding=p, bias=use_bias), norm_layer(dim), nn.ReLU(True)] if use_dropout: conv_block += [nn.Dropout(0.5)] p = 0 if padding_type == 'reflect': conv_block += [nn.ReflectionPad2d(1)] elif padding_type == 'replicate': conv_block += [nn.ReplicationPad2d(1)] elif padding_type == 'zero': p = 1 else: raise NotImplementedError('padding [%s] is not implemented' % padding_type) conv_block += [nn.Conv2d(dim, dim, kernel_size=3, padding=p, bias=use_bias), norm_layer(dim)] return nn.Sequential(*conv_block) def forward(self, x): """Forward function (with skip connections)""" out = x + self.conv_block(x) # add skip connections return out import functools norm_layer = functools.partial(nn.InstanceNorm2d, affine=False, track_running_stats=False) model = ResnetGenerator(3,3,64, norm_layer=norm_layer, use_dropout=False, n_blocks=9) model.load_state_dict(torch.load(path)) model.eval() model.cuda() model.half() trace_input = torch.ones(1,3,256,256).cuda() model = model.eval() jit_model = torch.jit.trace(model.float(), trace_input) torch.jit.save(jit_model, output_path) 

Reemplace las variables con las suyas propias:


  • ruta : la ruta a la iteración del modelo que te gusta.
    Necesitamos un archivo * G_A.pth - generador de fotos -> cómic.
  • output_path : el nombre del archivo para el modelo exportado, puede ser cualquier cosa con la extensión * .jit, lo principal es no olvidar dónde lo guardamos.

No es necesario exportar el generador de la última era de entrenamiento, tome aquel cuyos resultados le gusten más.

Luego, vaya a la consola, vaya a la carpeta con nuestro archivo y escriba:


 python jit.py 

Voila! El modelo está listo para familiarizarse con el mundo exterior.


Más información sobre antorcha jit

Documentación: https://pytorch.org/docs/stable/jit.html
En resumen, exportar a jit le permite serializar el modelo y no arrastrar el entorno de Python junto con él, todas las dependencias y módulos externos que podría usar. Excepto la antorcha, por supuesto.
Aunque lo alojaremos en un entorno python, los modelos jit se pueden usar en aplicaciones independientes.


Elección de alojamiento


Seré muy franco e inmediatamente admitiré: mi entusiasta interno del aprendizaje profundo murió en algún lugar en la segunda hora de explorar las posibilidades de hospedaje con soporte de GPU. Entonces, si alguien me dice un alojamiento económico de GPU sin servidor, estaré más que agradecido.

No planeaba pagar un servidor completo para mis experimentos, por lo tanto, solo estaba buscando soluciones sin servidor.


Después de terribles escalofríos en los planes de tarifas de varias páginas de Google y Amazon, mi elección recayó en algoritmia.com


Hay varias razones:


  • Web IDE es ideal para tontos, aunque terriblemente lento, porque debe esperar a que se complete la compilación para la verificación. Fuera de este tutorial, recomendaría probar todo localmente, ya que la mayoría de los errores ocurren en la etapa de descarga y guardado de archivos.


  • El número mínimo de opciones : es difícil morir de vejez prematura, sin haber leído la lista de opciones hasta el final.


  • Bueno, el último argumento : durante casi medio año de experimentos, todavía no he gastado un saldo inicial gratuito.



Aunque ahora al registrar una cuenta personal dan menos que el verano pasado, esto debería ser suficiente por un tiempo, y ciertamente para todos nuestros experimentos. Al final del mes, se otorgan más préstamos, lo que más de una vez me salvó de una posible ruina.


De las desventajas, vale la pena señalar que las tarjetas de video solo son las antiguas Tesla K80 con 12 GB de RAM, lo que impone las restricciones correspondientes. En cualquier caso, hasta que lleguemos a producción, ya entenderemos lo que necesitamos del servidor.


Implementar modelos


Bueno, a la batalla!


Registro


Vamos a https://algorithmia.com/signup y nos registramos. No estoy seguro de que haya una diferencia en qué profesión / tipo de cuenta elegir, pero si encuentra un combo de oro que le otorgue el máximo de créditos, ¡asegúrese de informarme en los comentarios!


Descargar modelo


Después del registro, estaremos en su perfil.
Necesitamos crear carpetas para el modelo y las imágenes que generará.
Para hacer esto, seleccione Orígenes de datos en el menú de la izquierda.


Haga clic en Nueva fuente de datos -> Recopilación de datos alojados
Pongamos nombre a la carpeta "Mis modelos".
Como resultado, deberíamos ser transferidos a una página con una lista de nuestras carpetas.


Cree otra carpeta: Nueva colección -> "photo2comics_out"


¡Es hora de descargar nuestro modelo recién exportado!
Vaya a la carpeta Mis modelos y arrastre el archivo del modelo al navegador, o seleccione Cargar archivos en el menú.


Ahora copie el enlace a nuestro modelo, nos será útil a continuación. Para hacer esto, haga clic en los puntos suspensivos a la derecha del nombre del archivo.


Los datos han terminado, vaya al algoritmo mismo.


Algoritmo


Regresamos al perfil haciendo clic en Inicio en el menú de la izquierda.


Luego, haga clic en Crear nuevo -> Algoritmo y seleccione el nombre de nuestro algoritmo. Completamos las opciones restantes como en la imagen a continuación.


Imagen de abajo


Haga clic en Crear nuevo algoritmo y seleccione WebIDE en la ventana que aparece.
Si accidentalmente cerró la ventana emergente, puede abrir el código fuente haciendo clic en Código fuente en el menú de nuestro algoritmo.


Eliminamos el código de la plantilla e insertamos el nuestro:


Un poco más de código para una copia aún más irreflexiva
 import Algorithmia import torch import torchvision import torchvision.transforms as transforms import cv2 from torch import * import uuid import gc import requests import numpy as np client = Algorithmia.client() #     file_path,      #   model_file   . def load_model(): file_path = "{      }" model_file = client.file(file_path).getFile().name model = torch.jit.load(model_file).half().cuda() return model model = load_model().eval() torch.cuda.empty_cache() torch.cuda.ipc_collect() torch.backends.cudnn.benchmark = True #    ,    , #     , # ..      def preprocessing(image_path, max_size): response = requests.get(image_path) response = response.content nparr = np.frombuffer(response, np.uint8) img_res = cv2.imdecode(nparr, cv2.IMREAD_COLOR) img_res = cv2.cvtColor(img_res, cv2.COLOR_BGR2RGB) x = img_res.shape[0] y = img_res.shape[1] #if image is bigger than the target max_size, downscale it if x>max_size and x<=y: y = y*(max_size/x) x = max_size if y>max_size and y<x: x = x*(max_size/y) y = max_size size = (int(y),int(x)) img_res = cv2.resize(img_res,size) t = Tensor(img_res/255.)[None,:] t = t.permute(0,3,1,2).half().cuda() # standartize t = (t-0.5)/0.5 return(t) def predict(input): gc.collect() with torch.no_grad(): res = model(input).detach() return res #   ,     #      def save_file(res, file_uri): #de-standartize res = (res*0.5)+0.5 tempfile = "/tmp/"+str(uuid.uuid4())+".jpg" torchvision.utils.save_image(res,tempfile) client.file(file_uri).putFile(tempfile) # API calls will begin at the apply() method, with the request body passed as 'input' # For more details, see algorithmia.com/developers/algorithm-development/languages def apply(input): processed_data= preprocessing(input["in"], input["size"]) res = predict(processed_data) save_file(res, input["out"]) input = None res = None processed_data = None gc.collect() return "Success" 

No olvide insertar un enlace al modelo descargado. Lo copiamos en la sección anterior al cargar el modelo.


Mientras está en WebIDE, haga clic en DEPENDENCIAS en la esquina superior derecha y reemplace el texto con una lista de nuestras dependencias:


 algorithmia>=1.0.0,<2.0 opencv-python six torch==1.3.0 torchvision numpy 

La versión de la antorcha debe ser la misma o más nueva que la que guardamos el modelo. De lo contrario, puede haber errores al importar el modelo jit.


Haga clic en GUARDAR, CONSTRUIR y espere a que se complete la compilación. Tan pronto como aparezca un mensaje sobre una compilación exitosa en la consola a continuación, podemos verificar el rendimiento del modelo enviando una solicitud de prueba a la consola:


 {"in":"https://cdn3.sportngin.com/attachments/photo/9226/3971/JABC-9u_medium.JPG", "out":"data://username/photo2comics_out/test.jpg", "size":512} 

Donde {nombre de usuario} es tu nombre de usuario. Si todo salió bien, "Success" aparecerá en la consola, y la imagen generada aparecerá en la carpeta que especificamos (en este caso photo2comics_out).


Resumen


¡Felicitaciones, diferimos de manera barata y enojada nuestro humilde modelo!


En el próximo número, nos haremos amigos del modelo de bot de telegramas y, finalmente, lanzaremos todo esto.
Si no puede esperar para probar el modelo, siempre puede leer la documentación oficial: https://algorithmia.com/developers/api/


Bueno, para pasar el tiempo hasta el próximo artículo, puedes meter algunos bots, modelos para los que publiqué en el Algoritmo:


@ selfie2animebot - Convierte una selfie en un anime
@pimpmyresbot : aumenta la resolución x2 (máximo a 1400x1400)
@photozoombot - Crea un video con zoom 3D a partir de una foto
@ photo2comicsbot - En realidad, el héroe de la ocasión


No olvides compartir los resultados, ideas y problemas encontrados en los comentarios.
Eso es todo por hoy. Hasta pronto!

Source: https://habr.com/ru/post/485824/


All Articles