Cara membuat bot yang mengubah foto menjadi komik. Bagian tiga. Server + GPU hosting gratis tanpa server

⇨ Bagian 1
⇨ Bagian 2


Yah, istirahat dan itu sudah cukup. Selamat datang kembali!


Dalam seri sebelumnya, Anda dan saya mengumpulkan data dan melatih model pertama kami.
Kemudian, ngeri dengan hasilnya, mereka melatih selusin lebih.
Sudah waktunya untuk menunjukkan kreasi kita kepada dunia!


Model Ekspor


Untuk memulainya, kita akan menyimpan kembali model generator dalam format yang sesuai sehingga kita tidak perlu menyeret deklarasi kelas ke hosting.


Buat file kecil dengan ekstensi * .py dan salin kode dari bawah spoiler di bawahnya.
Biarkan jit.py:


Kode yang akan disalin tanpa berpikir dengan mengganti path, output_path dengan Anda sendiri
#  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) 

Ganti variabel dengan Anda sendiri:


  • path - jalur ke model iterasi yang Anda sukai.
    Kami membutuhkan file * G_A.pth - generator foto -> buku komik.
  • output_path - nama file untuk model yang diekspor, bisa apa saja dengan ekstensi * .jit, yang utama adalah jangan lupa di mana kita menyimpannya.

Tidak perlu mengekspor generator dari era pelatihan terakhir, ambil generator yang hasilnya paling Anda sukai.

Selanjutnya, buka konsol, buka folder dengan file kami dan tulis:


 python jit.py 

Voila! Modelnya siap berkenalan dengan dunia luar.


Informasi lebih lanjut tentang obor jit

Dokumentasi: https://pytorch.org/docs/stable/jit.html
Singkatnya, mengekspor ke jit memungkinkan Anda membuat serial model dan tidak menyeret lingkungan python bersamanya, semua dependensi dan modul eksternal yang bisa digunakan. Kecuali obor, tentu saja.
Meskipun kita akan menyimpannya di lingkungan python, model jit dapat digunakan dalam aplikasi mandiri.


Pilihan Hosting


Saya akan berterus terang dan segera mengakui: penggemar pembelajaran mendalam internal saya meninggal di suatu tempat pada jam kedua dari mengeksplorasi kemungkinan hosting dengan dukungan GPU. Jadi, jika seseorang memberitahu saya hosting GPU tanpa Serverless yang murah, saya akan sangat berterima kasih.

Saya tidak berencana untuk membayar server penuh untuk percobaan saya, oleh karena itu saya hanya mencari solusi tanpa server.


Setelah menggeram tentang rencana tarif multi-halaman Google dan Amazon, pilihan saya jatuh pada algoritmia.com


Ada beberapa alasan:


  • IDE web sangat ideal untuk boneka, walaupun sangat lambat, karena Anda harus menunggu build untuk menyelesaikan verifikasi. Di luar tutorial ini, saya akan merekomendasikan pengujian semuanya secara lokal, karena sebagian besar kesalahan terjadi pada tahap mengunduh dan menyimpan file.


  • Jumlah minimum opsi - sulit untuk mati sejak usia dini, tidak membaca daftar opsi sampai akhir.


  • Argumen terakhir - selama hampir setengah tahun percobaan, saya masih belum menghabiskan saldo awal gratis.



Meskipun sekarang ketika mendaftarkan akun pribadi yang mereka berikan kurang dari musim panas lalu, ini masih cukup untuk sementara waktu, dan tentu saja untuk semua percobaan kami. Di akhir bulan, lebih banyak pinjaman dilemparkan, yang lebih dari sekali menyelamatkan saya dari potensi kehancuran.


Dari minus, perlu dicatat bahwa kartu video hanya ada Tesla K80 lama dengan RAM 12GB, yang memberlakukan pembatasan yang sesuai. Bagaimanapun, sampai kita mendapatkan produksi, kita akan sudah mengerti apa yang kita butuhkan dari server.


Menyebarkan model


Ya, untuk pertempuran!


Pendaftaran


Kami pergi ke https://algorithmia.com/signup dan mendaftar. Saya tidak yakin membuat perbedaan jenis akun profesi \ mana yang akan dipilih, tetapi jika Anda menemukan kombo emas yang memberikan kredit maksimum, pastikan untuk memberi tahu saya di komentar!


Unduh Model


Setelah pendaftaran, kami akan berada di profil Anda.
Kita perlu membuat folder untuk model dan gambar yang akan dihasilkannya.
Untuk melakukan ini, pilih Sumber Data di menu di sebelah kiri.


Klik Sumber Data Baru -> Pengumpulan Data yang Diinangi
Beri nama folder "Model Saya".
Akibatnya, kita harus ditransfer ke halaman dengan daftar folder kita.


Buat folder lain: Koleksi Baru -> "photo2comics_out"


Saatnya mengunduh model yang baru diekspor!
Buka folder Model Saya dan seret file model ke browser, atau pilih Unggah File dari menu.


Sekarang salin tautan ke model kami, ini akan berguna bagi kami di bawah ini. Untuk melakukan ini, klik pada elipsis di sebelah kanan nama file.


Data sudah selesai, buka algoritma itu sendiri.


Algoritma


Kami kembali ke profil dengan mengeklik Beranda di menu di sebelah kiri.


Selanjutnya, klik Buat Baru -> Algoritma dan pilih nama algoritma kami. Kami mengisi opsi yang tersisa seperti pada gambar di bawah ini.


Gambar di bawah ini


Klik Buat Algoritma Baru dan pilih WebIDE di jendela yang muncul.
Jika Anda menutup popup secara tidak sengaja, Anda dapat membuka kode sumber dengan mengklik Kode Sumber di menu algoritme kami.


Kami menghapus kode templat dan menyisipkan kode kami:


Beberapa kode lebih banyak lagi untuk penyalinan tanpa berpikir
 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" 

Jangan lupa untuk memasukkan tautan ke model yang diunduh. Kami menyalinnya di bagian sebelumnya saat memuat model.


Saat di WebIDE, klik DEPENDENSI di kanan atas dan ganti teks dengan daftar dependensi kami:


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

Versi obor harus sama atau lebih baru dari yang kita simpan model. Jika tidak, mungkin ada kesalahan saat mengimpor model jit.


Klik SIMPAN, BANGUN dan tunggu build selesai. Segera setelah pesan tentang build yang berhasil muncul di konsol di bawah, kami dapat memeriksa kinerja model dengan mengirimkan permintaan pengujian ke konsol:


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

Di mana {username} adalah nama pengguna Anda. Jika semuanya berjalan dengan baik, "Sukses" akan muncul di konsol, dan gambar yang dihasilkan akan muncul di folder yang kami tentukan (dalam hal ini photo2comics_out).


Ringkasan


Selamat, kami dengan murah dan marah menangguhkan model kami yang rendah hati!


Dalam edisi berikutnya, kami akan berteman dengan model bot telegram dan, akhirnya, akan merilis semua hal ini.
Jika Anda tidak sabar untuk mencoba modelnya, Anda selalu dapat membaca dokumentasi resmi: https://algorithmia.com/developers/api/


Nah, untuk menghabiskan waktu sampai artikel berikutnya, Anda dapat menyodok beberapa bot, model yang saya posting di Algoritma:


@ selfie2animebot - Mengubah selfie menjadi anime
@pimpmyresbot - Meningkatkan resolusi x2 (maksimal 1400x1400)
@photozoombot - Membuat video zoom 3D dari satu foto
@ photo2comicsbot - Sebenarnya, pahlawan acara itu


Jangan lupa untuk membagikan hasil, ide dan masalah yang ditemui dalam komentar.
Itu saja untuk hari ini. Sampai ketemu lagi!

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


All Articles