Wie man einen Bot macht, der ein Foto in einen Comic verwandelt. Teil drei. Kostenloses serverloses + GPU-Hosting-Modell

⇨ Teil 1
⇨ Teil 2


Gut ausgeruht und das ist genug. Willkommen zurück!


In der vorherigen Serie haben Sie und ich Daten gesammelt und unser erstes Modell trainiert.
Dann trainierten sie, entsetzt über die Ergebnisse, ein Dutzend mehr.
Es ist Zeit, der Welt unsere Schöpfung zu zeigen!


Modell exportieren


Zunächst speichern wir das Generatormodell in einem geeigneten Format, damit wir keine Klassendeklarationen auf das Hosting ziehen müssen.


Erstellen Sie eine kleine Datei mit der Erweiterung * .py und kopieren Sie den Code unter dem Spoiler darunter hinein.
Lass es jit.py sein:


Der Code, der gedankenlos durch Ersetzen von path, output_path durch Ihren eigenen Code kopiert werden soll
#  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) 

Ersetzen Sie die Variablen durch Ihre eigenen:


  • Pfad - Der Pfad zur gewünschten Modelliteration.
    Wir brauchen eine Datei * G_A.pth - Foto Generator -> Comic.
  • output_path - der Dateiname für das exportierte Modell, es kann alles mit der Endung * .jit sein, die Hauptsache ist, nicht zu vergessen, wo wir es gespeichert haben.

Es ist nicht notwendig, den Generator aus der letzten Ära des Trainings zu exportieren. Nehmen Sie den Generator, dessen Ergebnisse Ihnen am besten gefallen.

Als nächstes gehe zur Konsole, gehe in den Ordner mit unserer Datei und schreibe:


 python jit.py 

Voila! Das Modell ist bereit, die Außenwelt kennenzulernen.


Weitere Informationen zu Fackel Jit

Dokumentation: https://pytorch.org/docs/stable/jit.html
Kurz gesagt, beim Exportieren nach JIT können Sie das Modell serialisieren und nicht die Python-Umgebung, alle Abhängigkeiten und externen Module, die verwendet werden könnten, mitziehen. Außer Fackel natürlich.
Obwohl wir es in einer Python-Umgebung hosten werden, können JIT-Modelle in eigenständigen Anwendungen verwendet werden.


Hosting-Wahl


Ich werde sehr offen sein und sofort zugeben: Mein interner Deep-Learning-Enthusiast starb irgendwann in der zweiten Stunde, als er die Möglichkeiten des Hostings mit GPU-Unterstützung erkundete. Wenn mir jemand sagt, dass ich ein kostengünstiges GPU-Hosting ohne Server habe, bin ich mehr als dankbar.

Ich hatte nicht vor, für meine Experimente einen vollwertigen Server zu bezahlen, deshalb suchte ich nur nach serverlosen Lösungen.


Nachdem ich mich in den mehrseitigen Tarifplänen von Google und Amazon qualvoll eingeschlichen hatte, fiel meine Wahl auf algorithmia.com


Dafür gibt es mehrere Gründe:


  • Die Web-IDE ist ideal für Dummies, wenn auch fürchterlich langsam, da Sie warten müssen, bis der Build für die Überprüfung abgeschlossen ist. Außerhalb dieses Tutorials würde ich empfehlen, alles lokal zu testen, da die meisten Fehler beim Herunterladen und Speichern von Dateien auftreten.


  • Die minimale Anzahl von Optionen - es ist schwierig, an vorzeitigem Alter zu sterben, ohne die Liste der Optionen bis zum Ende durchgelesen zu haben.


  • Nun, das letzte Argument - seit fast einem halben Jahr habe ich immer noch kein freies Startguthaben ausgegeben.



Obwohl sie jetzt bei der Registrierung eines persönlichen Kontos weniger geben als im letzten Sommer, sollte dies für eine Weile und mit Sicherheit für alle unsere Experimente ausreichen. Ende des Monats werden immer mehr Kredite vergeben, was mich mehr als einmal vor einem möglichen Ruin bewahrt hat.


Von den Minuspunkten ist anzumerken, dass es sich bei den Grafikkarten nur um das alte Tesla K80 mit 12 GB RAM handelt, was entsprechende Einschränkungen auferlegt. In jedem Fall werden wir bis zur Serienreife bereits verstehen, was wir vom Server benötigen.


Modelle bereitstellen


Nun zum Kampf!


Registrierung


Wir gehen zu https://algorithmia.com/signup und registrieren uns. Ich bin mir nicht sicher, ob es einen Unterschied macht, welchen Beruf / Konto-Typ ich auswähle. Wenn Sie jedoch eine Gold-Kombination finden, die maximale Credits bietet, lassen Sie es mich in den Kommentaren wissen!


Modell herunterladen


Nach der Registrierung werden wir in Ihrem Profil sein.
Wir müssen Ordner für das Modell und die Bilder erstellen, die es erzeugen wird.
Wählen Sie dazu im Menü links Datenquellen.


Klicken Sie auf Neue Datenquelle -> Gehostete Datenerfassung
Nennen wir den Ordner „My Models“.
Aus diesem Grund sollten wir auf eine Seite mit einer Liste unserer Ordner weitergeleitet werden.


Erstellen Sie einen anderen Ordner: Neue Sammlung -> "photo2comics_out"


Es ist Zeit, unser frisch exportiertes Modell herunterzuladen!
Gehen Sie zum Ordner Meine Modelle und ziehen Sie die Modelldatei in den Browser oder wählen Sie im Menü die Option Dateien hochladen.


Kopieren Sie nun den Link zu unserem Modell, er wird uns weiter unten nützlich sein. Klicken Sie dazu auf die Auslassungspunkte rechts neben dem Dateinamen.


Die Daten sind vorbei, gehen Sie zum Algorithmus selbst.


Algorithmus


Wir kehren zum Profil zurück, indem wir links im Menü auf Home klicken.


Klicken Sie anschließend auf Neu erstellen -> Algorithmus und wählen Sie den Namen unseres Algorithmus aus. Wir füllen die restlichen Optionen wie im Bild unten aus.


Bild unten


Klicken Sie auf Neuen Algorithmus erstellen und wählen Sie im angezeigten Fenster WebIDE aus.
Wenn Sie das Popup versehentlich geschlossen haben, können Sie den Quellcode öffnen, indem Sie im Menü unseres Algorithmus auf Quellcode klicken.


Wir entfernen den Vorlagencode und fügen unseren ein:


Noch mehr Code für noch gedankenloses Kopieren
 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" 

Vergessen Sie nicht, einen Link zum heruntergeladenen Modell einzufügen. Wir haben es im vorherigen Abschnitt beim Laden des Modells kopiert.


Klicken Sie in WebIDE oben rechts auf DEPENDENCIES und ersetzen Sie den Text durch eine Liste unserer Abhängigkeiten:


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

Die Brennerversion muss dieselbe oder eine neuere sein als die, auf der wir das Modell gespeichert haben. Andernfalls kann es beim Import von JIT-Modellen zu Fehlern kommen.


Klicken Sie auf SPEICHERN, ERSTELLEN und warten Sie, bis der Build abgeschlossen ist. Sobald in der folgenden Konsole eine Meldung über einen erfolgreichen Build angezeigt wird, können Sie die Leistung des Modells überprüfen, indem Sie eine Testanforderung an die Konsole senden:


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

Wobei {username} Ihr Benutzername ist. Wenn alles geklappt hat, wird in der Konsole "Erfolgreich" und das generierte Bild in dem von uns angegebenen Ordner angezeigt (in diesem Fall photo2comics_out).


Zusammenfassung


Herzlichen Glückwunsch, wir haben unser bescheidenes Modell billig und verärgert verschoben!


In der nächsten Ausgabe werden wir uns mit dem Telegramm-Bot-Modell anfreunden und schließlich all dieses Zeug veröffentlichen.
Wenn Sie es kaum erwarten können, das Modell auszuprobieren, können Sie jederzeit die offizielle Dokumentation lesen: https://algorithmia.com/developers/api/


Nun, um die Zeit bis zum nächsten Artikel zu vertreiben, können Sie einige Bots stöbern, Modelle, für die ich auf dem Algorithmus gepostet habe:


@ selfie2animebot - Verwandelt ein Selfie in einen Anime
@pimpmyresbot - Erhöht die x2-Auflösung (maximal auf 1400x1400)
@photozoombot - Erstellt ein 3D- Zoomvideo aus einem Foto
@ photo2comicsbot - Eigentlich der Held der Gelegenheit


Vergessen Sie nicht, die in den Kommentaren festgestellten Ergebnisse, Ideen und Probleme mitzuteilen.
Das ist alles für heute. Bis bald

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


All Articles