Como fazer um bot que transforma uma foto em quadrinhos. Parte três. Modelo de hospedagem sem servidor + GPU gratuito

⇨ Parte 1
⇨ Parte 2


Bem, descansou e é o suficiente. Bem-vindo de volta!


Na série anterior, você e eu coletamos dados e treinamos nosso primeiro modelo.
Horrorizados com os resultados, eles treinaram uma dúzia a mais.
É hora de mostrar nossa criação ao mundo!


Modelo de Exportação


Para começar, salvaremos novamente o modelo do gerador em um formato adequado, para que não tenhamos que arrastar as declarações de classe para a hospedagem.


Crie um arquivo pequeno com a extensão * .py e copie o código abaixo do spoiler abaixo.
Seja jit.py:


O código a ser copiado sem pensar substituindo path, output_path por seu próprio
#  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) 

Substitua as variáveis ​​por suas próprias:


  • path - o caminho para a iteração do modelo que você gosta.
    Precisamos de um arquivo * G_A.pth - gerador de fotos -> história em quadrinhos.
  • output_path - o nome do arquivo do modelo exportado, pode ser qualquer coisa com a extensão * .jit, o principal é não esquecer onde o salvamos.

Não é necessário exportar o gerador da última era do treinamento, pegar aquele cujos resultados você mais gosta.

Em seguida, vá para o console, vá para a pasta com o nosso arquivo e escreva:


 python jit.py 

Voila! O modelo está pronto para se familiarizar com o mundo exterior.


Mais informações sobre tocha jit

Documentação: https://pytorch.org/docs/stable/jit.html
Em resumo, exportar para jit permite serializar o modelo e não arrastar o ambiente python junto com ele, todas as dependências e módulos externos que ele poderia usar. Exceto a tocha, é claro.
Embora o hospedemos em um ambiente python, os modelos jit podem ser usados ​​em aplicativos independentes.


Escolha de hospedagem


Serei muito franco e imediatamente admito: meu entusiasta interno de aprendizado profundo morreu em algum lugar na segunda hora de explorar as possibilidades de hospedagem com suporte à GPU. Portanto, se alguém me disser hospedagem barata de GPU sem servidor, ficarei mais do que agradecido.

Não planejava pagar por um servidor completo para minhas experiências, portanto, estava procurando apenas soluções sem servidor.


Depois de rastejantes excruciantes nos planos tarifários de várias páginas do Google e da Amazon, minha escolha caiu em algoritmia.com


Existem várias razões:


  • O Web IDE é ideal para manequins, embora seja muito lento, porque você precisa aguardar a conclusão da compilação para verificação. Fora deste tutorial, eu recomendaria testar tudo localmente, pois a maioria dos erros ocorre no estágio de download e salvamento de arquivos.


  • O número mínimo de opções - é difícil morrer de velhice prematura, não tendo lido a lista de opções até o fim.


  • Bem, o último argumento - durante quase meio ano de experimentos, eu ainda não gastei um saldo inicial gratuito.



Embora agora, ao registrar uma conta pessoal, eles dêem menos do que no verão passado, isso ainda deve ser suficiente por um tempo e certamente para todos os nossos experimentos. No final do mês, mais empréstimos estão sendo depositados, o que mais de uma vez me salvou de uma potencial ruína.


Dos pontos negativos, vale ressaltar que as placas de vídeo existem apenas o antigo Tesla K80 com 12GB de RAM, o que impõe restrições correspondentes. De qualquer forma, até chegarmos à produção, já entenderemos o que precisamos do servidor.


Implantar modelos


Bem, para a batalha!


Registo


Vamos para https://algorithmia.com/signup e nos registramos. Não sei se faz diferença qual tipo de profissão / conta escolher, mas se você encontrar uma combinação de ouro que ofereça o máximo de créditos, informe-me nos comentários!


Download do modelo


Após o registro, entraremos em seu perfil.
Precisamos criar pastas para o modelo e as imagens que ele irá gerar.
Para fazer isso, selecione Fontes de dados no menu à esquerda.


Clique em Nova fonte de dados -> Coleta de dados hospedados
Vamos nomear a pasta "Meus modelos".
Como resultado, devemos ser transferidos para uma página com uma lista de nossas pastas.


Crie outra pasta: Nova coleção -> “photo2comics_out”


É hora de baixar o nosso modelo recém-exportado!
Vá para a pasta Meus modelos e arraste o arquivo de modelo para o navegador ou selecione Upload de arquivos no menu.


Agora copie o link para o nosso modelo, será útil para nós abaixo. Para fazer isso, clique nas reticências à direita do nome do arquivo.


Os dados terminaram, vá para o próprio algoritmo.


Algoritmo


Retornamos ao perfil clicando em Início no menu à esquerda.


Em seguida, clique em Criar novo -> Algoritmo e selecione o nome do nosso algoritmo. Nós preenchemos as opções restantes, como na figura abaixo.


Foto abaixo


Clique em Criar novo algoritmo e selecione WebIDE na janela que aparece.
Se você acidentalmente fechou o pop-up, é possível abrir o código-fonte clicando em Código-fonte no menu do nosso algoritmo.


Removemos o código do modelo e inserimos o nosso:


Um pouco mais de código para cópias ainda mais imprudentes
 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" 

Não se esqueça de inserir um link para o modelo baixado. Nós o copiamos na seção anterior ao carregar o modelo.


Enquanto estiver no WebIDE, clique em DEPENDENCIES no canto superior direito e substitua o texto por uma lista de nossas dependências:


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

A versão da tocha deve ser a mesma ou mais recente que aquela em que salvamos o modelo. Caso contrário, pode haver erros ao importar o modelo jit.


Clique em SALVAR, CONSTRUIR e aguarde a conclusão da compilação. Assim que uma mensagem sobre uma compilação bem-sucedida aparecer no console abaixo, podemos verificar o desempenho do modelo enviando uma solicitação de teste ao console:


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

Onde {nomedeusuário} é o seu nome de usuário. Se tudo correu bem, “Success” aparecerá no console e a imagem gerada aparecerá na pasta que especificamos (neste caso, photo2comics_out).


Sumário


Parabéns, adiamos barata e com raiva nosso humilde modelo!


Na próxima edição, faremos amizade com o modelo de bot de telegrama e, finalmente, lançaremos tudo isso.
Se você não pode esperar para experimentar o modelo, sempre pode ler a documentação oficial: https://algorithmia.com/developers/api/


Bem, para passar o tempo até o próximo artigo, você pode cutucar alguns bots, modelos para os quais eu postei no Algoritmo:


@ selfie2animebot - Transforma uma selfie em um anime
@pimpmyresbot - aumenta a resolução x2 (máximo para 1400x1400)
@photozoombot - Cria vídeo com zoom 3D a partir de uma foto
@ photo2comicsbot - Na verdade, o herói da ocasião


Não se esqueça de compartilhar os resultados, idéias e problemas encontrados nos comentários.
Isso é tudo por hoje. Até breve!

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


All Articles