Pada artikel ini saya akan menjelaskan bagaimana membangun
situs web ASP.NET Core , yang menggunakan AI untuk menghasilkan lirik lagu yang unik dengan mengklik tombol, dan memungkinkan pengguna memilih lagu terbaik.
Jaringan saraf
Sekitar 2,5 bulan yang lalu
OpenAI menerbitkan
posting blog , di mana mereka menunjukkan hampir mustahil: model pembelajaran yang mendalam, yang dapat menulis artikel, tidak bisa dibedakan dari yang ditulis oleh manusia. Teks yang dihasilkannya sangat mengesankan, sehingga saya harus memeriksa kalender untuk memastikan itu bukan lelucon April Mop (ingat Anda itu Februari, dan Seattle tertutup salju).

Mereka tidak merilis jaringan saraf terbesar dengan lebih dari 1 miliar parameter yang mereka bangun hari ini (keputusan yang sangat kontroversial), tetapi mereka membuka sumber versi parameter 117M yang lebih kecil pada GitHub di bawah lisensi MIT. Model ini memiliki nama yang sangat tak terlupakan: GPT-2 .
Jadi, sekitar sebulan yang lalu, ketika saya mencoba memikirkan proyek keren apa yang bisa saya buat dengan TensorFlow, jaringan itu menjadi titik awal. Jika sudah bisa menghasilkan teks bahasa Inggris, seharusnya tidak terlalu sulit untuk menyempurnakannya untuk menghasilkan lirik lagu, jika ada dataset yang cukup besar.
Bagaimana cara kerja GPT-2?
Ada beberapa pencapaian penting dalam penelitian pembelajaran dalam, yang memungkinkan GPT-2 menjadi mungkin:
Belajar mandiri
Teknik ini mendapatkan namanya diselesaikan oleh Yan LeCunn hanya beberapa hari setelah saya menulis versi pertama artikel ini. Ini adalah teknik yang sangat kuat, yang dapat diterapkan pada dasarnya semua jenis data dunia nyata. Untuk melatih GPT-2 OpenAI mengumpulkan puluhan gigabyte artikel dari berbagai sumber, yang diputuskan di Reddit.
Secara konvensional, seseorang harus memiliki manusia untuk melalui semua artikel ini, dan, misalnya, menandainya sebagai "positif" atau "negatif". Kemudian mereka akan mengajarkan jaringan saraf dengan cara yang diawasi untuk mengklasifikasikan artikel ini dengan cara yang sama seperti manusia.

Gagasan baru di sini adalah untuk membuat model pembelajaran yang mendalam, yang memiliki pemahaman tingkat tinggi tentang data Anda, Anda hanya merusak data, dan menugaskan model untuk mengembalikan yang asli. Ini membuat model memahami koneksi antara potongan data, dan konteks sekitarnya.
Mari kita ambil teks sebagai contoh. GPT-2 mengambil sampel teks asli, mengambil 15% token untuk dikorupsi, lalu menutupi 80% darinya (misalnya mengganti dengan token topeng khusus, biasanya ___), menggantikan 10% dengan beberapa token acak lain dari kamus, dan menjaga 10% sisanya tetap utuh. Ambil aku melempar bola, dan jatuh ke rumput . Setelah korupsi mungkin terlihat seperti ini: Saya melempar bola mobil, dan ___ ke rumput . Dalam istilah awam, untuk mendapatkan jaringan mengembalikan yang asli, perlu belajar, bahwa sesuatu yang dibuang kemungkinan akan jatuh, dan bola mobil adalah sesuatu yang sangat tidak biasa dalam konteksnya.
Model yang dilatih seperti itu bagus dalam menghasilkan / melengkapi data parsial, tetapi fitur tingkat tinggi yang dipelajarinya (sebagai output dari lapisan dalam) dapat digunakan untuk tujuan lain dengan menambahkan satu atau dua lapisan di atasnya, dan fine-tuning hanya lapisan terakhir yang baru pada dataset aktual, lebih kecil , dan bertanda manusia dengan cara konvensional.
Sedikit perhatian pada diri sendiri
GPT-2 menggunakan sesuatu yang disebut jarang perhatian diri. Pada intinya, ini adalah teknik, yang memungkinkan jaringan saraf memproses input besar untuk fokus pada beberapa bagian lebih dari yang lain. Dan jaringan belajar di mana ia harus "melihat" selama pelatihan. Mekanisme perhatian lebih dijelaskan dalam posting blog ini .
Bagian yang jarang dalam judul bagian ini mengacu pada batasan segmen input yang dapat dipilih mekanisme perhatian. Perhatian awal dapat memilih dari seluruh input. Itu menyebabkan matriks bobotnya menjadi O (input_size ^ 2), yang tumbuh sangat cepat dengan ukuran input. Perhatian yang jarang biasanya membatasi hal itu dalam beberapa cara. Untuk informasi lebih lanjut tentang itu, lihat posting blog OpenAI lain.
Perhatian dalam GPT-2 adalah multi-head . Bayangkan Anda bisa memiliki satu atau dua mata tambahan yang bisa Anda gunakan untuk memeriksa apa yang ada di paragraf terakhir tanpa berhenti membaca yang ada saat ini.
Lebih banyak
Koneksi residual , penyandian pasangan Byte , prediksi kalimat berikutnya, dan banyak lagi.
Porting GPT-2 (dan mengonversi Python secara umum)
Kode model asli dalam Python, tapi saya seorang pria C #. Untungnya, kode sumbernya cukup mudah dibaca, dan intinya hanya dalam 5 file, mungkin total 500 baris. Jadi saya membuat proyek .NET Standard baru, menginstal Gradient (pengikatan TensorFlow untuk .NET), dan mengonversi file-file tersebut baris demi baris ke C #. Butuh waktu sekitar 2 jam. Satu-satunya hal pythonic yang tersisa dalam kode adalah penggunaan modul regex Python dari pip (manajer paket yang paling umum digunakan untuk Python), karena saya tidak ingin membuang waktu mempelajari seluk-beluk ekspresi reguler Python ( seolah-olah itu tidak cukup. untuk menangani. NET yang sudah ).
Sebagian besar konversi terdiri dari mendefinisikan kelas yang serupa, menambahkan tipe, dan menulis ulang pemahaman daftar Python ke dalam konstruksi LINQ yang sesuai. Selain LINQ dari perpustakaan standar, saya menggunakan MoreLinq , yang sedikit memperluas apa yang bisa dilakukan LINQ Sebagai contoh:
bs = list(range(ord("!"), ord("~")+1)) + list(range(ord("Β‘"), ord("Β¬")+1)) + list(range(ord(""), ord("ΓΏ")+1))
berubah menjadi:
var bs = Range('!', '~' - '!' + 1) .Concat(Range('Β‘', 'Β¬' -'Β‘' + 1)) .Concat(Range('', 'ΓΏ' - '' + 1)) .ToList();
Hal lain yang harus saya lawan adalah perbedaan antara cara Python menangani rentang, dan fitur Rentang dan indeks baru di C # 8 mendatang, yang saya temukan saat men-debug proses awal saya: di C # 8 akhir rentang inklusif , sementara di Python itu eksklusif (untuk memasukkan elemen terakhir dalam Python Anda harus menghilangkan sisi kanan .. ekspresi).
Ada dua hal yang sulit dalam ilmu komputer: pembatalan cache, penamaan, dan kesalahan satu-per-satu.
Sayangnya, sumber drop asli tidak mengandung pelatihan atau bahkan kode fine-tuning, tetapi Neil Shepperd memberikan fine-tuner sederhana pada GitHub- nya, yang harus saya porting juga. Bagaimanapun, hasil dari usaha itu adalah kode C # , yang dapat digunakan untuk bermain dengan GPT-2 , sekarang menjadi bagian dari repositori Gradient Samples .
Inti dari latihan porting adalah dua kali lipat: setelah porting seseorang dapat bermain dengan kode model dalam C # IDE favoritnya , dan untuk menunjukkan, bahwa sekarang mungkin untuk mendapatkan model pembelajaran mendalam yang canggih yang bekerja dalam kebiasaan .NET memproyeksikan tak lama setelah rilis (antara penurunan kode GPT-2 dan rilis pertama Lagu Billion - hanya sedikit lebih dari sebulan).
Menyempurnakan lirik lagu
Ada beberapa cara seseorang bisa mendapatkan kumpulan lirik lagu yang besar. Anda dapat mengikis salah satu situs web Internet yang menghostingnya dengan parser HTML, menariknya dari koleksi karaoke Anda, atau file mp3. Untungnya, seseorang melakukannya untuk kita. Saya menemukan beberapa dataset lirik yang disiapkan di Kaggle . β Setiap lagu yang telah Anda dengar β tampaknya menjadi yang terbesar. Mencoba untuk menyempurnakan GPT-2 untuk itu, saya menghadapi dua masalah.
Pembacaan CSV
Ya, Anda membacanya dengan benar, penguraian CSV adalah masalah . Awalnya, saya ingin menggunakan ML.NET, perpustakaan Microsoft baru untuk pembelajaran mesin, untuk membaca file. Namun, setelah membaca sekilas melalui dokumentasi, dan mengaturnya, saya menyadari, bahwa itu gagal memproses jeda baris dalam lagu dengan benar. Tidak peduli apa yang saya lakukan, itu berjuang setelah beberapa ratus contoh, dan mulai mencampur potongan lirik dengan judul dan artis.
Jadi saya harus beralih ke perpustakaan tingkat bawah, yang sebelumnya saya punya pengalaman lebih baik: CsvHelper . Ini menyediakan antarmuka seperti DataReader . Anda dapat melihat kode menggunakannya di sini . Pada dasarnya, Anda membuka file, mengkonfigurasi CsvReader , dan kemudian interleave panggilan ke .Read () dengan panggilan ke .GetField (fieldName) .
Lagu pendek
Sebagian besar lagu pendek dibandingkan dengan artikel rata-rata dalam dataset asli yang digunakan oleh OpenAI. Pelatihan GPT-2 lebih efisien pada potongan teks besar, jadi saya harus menggabungkan beberapa lagu menjadi potongan teks berkelanjutan untuk memberi mereka makan kepada pelatih. OpenAI juga tampaknya menggunakan teknik ini, jadi mereka memiliki token khusus <| endoftext |> , yang bertindak sebagai pemisah antara teks lengkap di dalam chunk, dan berfungsi ganda sebagai token awal. Saya menggabungkan beberapa lagu hingga sejumlah token tercapai, lalu mengembalikan seluruh potongan untuk dimasukkan ke dalam data pelatihan. Kode yang relevan ada di sini .
Persyaratan perangkat keras untuk penyetelan
Bahkan versi yang lebih kecil dari GPT-2 berukuran besar. Dengan 12GB RAM GPU, saya hanya dapat mengatur ukuran bets menjadi 2 (mis. Melatih dua bong sekaligus, ukuran bets yang lebih besar meningkatkan kinerja GPU dan hasil pelatihan). 3 akan membuang memori di CUDA. Dan butuh setengah hari untuk menyesuaikannya dengan kinerja yang diinginkan pada V100 saya. Bonusnya adalah Anda dapat melihat progresnya, karena setiap sekarang dan kemudian kode pelatihan mengeluarkan beberapa sampel yang dihasilkan, yang dimulai dengan teks sederhana, dan semakin terlihat seperti lirik lagu saat pelatihan berlangsung.
Saya belum mencobanya, tetapi pelatihan pada CPU mungkin akan sangat lambat .
Model yang sudah disetel sebelumnya
Ketika saya sedang mempersiapkan posting blog ini, saya menyadari akan lebih baik untuk tidak memaksa semua orang menghabiskan waktu berjam-jam memperbaiki model lirik , jadi saya merilis yang sudah disetel pada repositori Billion Songs . Jika Anda hanya mencoba menjalankan Billion Lagu, Anda bahkan tidak perlu mengunduhnya secara manual. Proyek akan melakukannya untuk Anda secara default.
model setengah terlatih bermain HAL9000 pada sayaAku bersumpah, aku seharusnya menulis surat kepadamu
Dan aku bersumpah, aku bersumpah
Anda menghancurkannya sekarang, saya harap Anda berhasil
Dan saya harap impian Anda, saya harap Anda bermimpi, saya harap Anda bermimpi. Saya harap Anda bermimpi. Saya harap Anda bermimpi
Tentang
apa yang saya pergi. Saya pergi Saya pergi Aku pergi, aku akan, aku akan, aku akan, aku akan, aku akan, aku akan,
Aku pergi, aku pergi, aku akan ...
Membuat situs web
Oke! Itu terlihat seperti lagu (semacam), sekarang mari kita membuat situs web!
Karena saya tidak berencana menyediakan API apa pun, saya memilih templat Razor Pages sebagai ganti MVC. Saya mengaktifkan otorisasi juga, karena kami akan memungkinkan pengguna untuk memilih lirik terbaik dan memiliki chart 10 Besar.
Bergegas MVP, saya pergi ke depan dan membuat halaman web Song.cshtml, yang tujuannya untuk saat ini adalah dengan hanya memanggil GPT-2 dan mendapatkan lagu acak. Tata letak halaman itu sepele, dan pada dasarnya terdiri dari lagu dan judulnya:
@page "/song/{id}" @model BillionSongs.Pages.SongModel</p> @{ ViewData["Title"] = @Model.Song.Title ?? "Untitled"; } <article style="text-align: center"> <h3>@(Model.Song.Title ?? "Untitled")</h3> <pre>@Model.Song.Lyrics</pre> </article>
Sekarang karena saya suka kode saya dapat digunakan kembali, saya membuat antarmuka, yang akan membiarkan saya memasang generator lirik yang berbeda nantinya, yang akan disuntikkan oleh ASP.NET ke SongModel.
interface ILyricsGenerator { Task<string> GenerateLyrics(uint song, CancellationToken cancellation); }
Menghilangkan judul lagu untuk saat ini, yang perlu kita lakukan adalah mendaftar Gpt2LyricsGenerator di Startup. Konfigurasikan Layanan dan panggil dari SongModel . Jadi mari kita mulai menggunakan generator. Dan hal pertama yang perlu kita pastikan, adalah yang kita miliki
Generasi lirik berulang
Karena saya membuat pernyataan berani dalam judul, bahwa itu akan menjadi lebih dari 1 miliar lagu, bahkan tidak berpikir tentang menghasilkan dan menyimpan semuanya. Pertama, tanpa metadata apa pun, itu akan mengambil sendiri lebih dari 1TB ruang disk. Kedua, butuh ~ 3 menit di nettop saya untuk menghasilkan lagu baru, jadi akan butuh selamanya untuk menghasilkan semuanya. Dan saya ingin dapat mengubah miliar itu menjadi trilyun dengan beralih ke Int64 jika diperlukan! Bayangkan kita bisa menghasilkan 1 sen per lagu, dengan 1 triliun lagu? Itu akan lebih dari PDB tahunan dunia saat ini!
Sebaliknya, yang perlu kita lakukan adalah memastikan, bahwa GPT-2 menghasilkan lagu yang sama berulang-ulang, mengingat id -nya, yang saya tentukan di rute. Untuk itu TensorFlow memberikan kemampuan untuk mengatur seed dari generator nomor internal kapan saja melalui fungsi tf.set_random_seed seperti ini: tf.set_random_seed (songNumber) . Lalu saya ingin memanggil Gpt2Sampler.SampleSequence , untuk mendapatkan teks lagu yang disandikan, mendekodekannya, dan mengembalikan hasilnya, sehingga melengkapi Gpt2LyricsGenerator .
Sayangnya, pada percobaan pertama itu tidak berhasil seperti yang diharapkan. Setiap kali saya menekan tombol refresh, teks unik baru akan dikembalikan pada halaman. Setelah melakukan sedikit debug, akhirnya saya menemukan, bahwa TensorFlow 1.X memiliki masalah signifikan dengan kemampuan reproduksi: banyak operasi memiliki status internal, yang tidak terpengaruh oleh set_random_seed dan sulit dijangkau untuk diatur ulang.
Inisialisasi ulang variabel model membantu mengimbangi masalah itu, tetapi juga berarti, bahwa sesi harus diciptakan kembali, dan bobot model harus dimuat ulang pada setiap panggilan. Memuat kembali sesi sebesar itu menyebabkan kebocoran memori raksasa. Untuk menghindari mencari penyebabnya dalam kode sumber TensorFlow C ++, alih-alih melakukan pembuatan teks dalam proses, saya memutuskan untuk menelurkan proses baru dengan Process.Start , menghasilkan teks di sana, dan membacanya dari output standar. Sampai cara untuk mengatur ulang keadaan model di TensorFlow distabilkan, ini akan menjadi cara untuk pergi.
Jadi saya berakhir dengan dua kelas: Gpt2LyricsGenerator , yang mengimplementasikan ILyricsGenerator dari atas dengan menelurkan instance baru BillionSongs.exe dengan parameter baris perintah, yang mencakup id lagu, dan akhirnya instantiate Gpt2TextGenerator , yang sebenarnya memanggil GPT-2 untuk menghasilkan lirik, dan cukup cetak saja.
Sekarang menyegarkan halaman selalu memberi saya teks yang sama.
Berurusan dengan 3 menit waktu untuk menghasilkan lagu
Betapa mengerikan pengalaman pengguna itu! Anda pergi ke situs web, klik "Buat Lagu Baru", dan sama sekali tidak ada yang terjadi selama 3 (!) Menit sementara nettop saya mengambil waktu untuk menghasilkan lirik lagu yang Anda minta.
Saya memecahkan masalah ini pada beberapa tingkatan:
Lagu pregenerasi
Seperti yang disebutkan di atas, Anda tidak dapat membuat pregenerasi semua lagu, dan menyajikannya dari basis data. Dan Anda tidak bisa hanya menghasilkan sesuai permintaan, karena itu lambat. Jadi apa yang bisa kamu lakukan?
Sederhana! Karena cara utama bagi pengguna untuk melihat lagu baru adalah dengan mengklik tombol "Jadikan Acak", mari kita membuat banyak lagu sebelumnya, memasukkannya ke ConcurrentQueue , dan biarkan lagu pop "Make Random" darinya. Sementara jumlah pengunjung rendah, server akan mengambil waktu di antara mereka untuk menghasilkan beberapa lagu, yang kemudian akan mudah diakses.
Trik lain, yang saya gunakan adalah untuk mengulangi antrian itu beberapa kali, sehingga banyak pengguna dapat melihat lagu pregenerasi yang sama. Satu hanya perlu menjaga keseimbangan antara penggunaan RAM, dan berapa kali pengguna harus mengklik "Jadikan Acak" untuk melihat sesuatu yang telah dilihatnya sebelumnya. Saya hanya memilih 50.000 lagu sebagai jumlah yang masuk akal, yang hanya membutuhkan 50MB RAM, sambil memberikan cukup banyak klik untuk dilalui.
Saya menerapkan fungsi itu di kelas PregeneratedSongProvider : IRandomSongProvider (antarmuka disuntikkan ke dalam kode, bertanggung jawab untuk menangani tombol "Jadikan Acak").
Caching
Lagu-lagu pregenerated di-cache ke memori, tapi saya juga mengatur header cache HTTP ke publik untuk membiarkan browser, dan CDN (saya menggunakan CloudFlare) cache itu untuk menghindari tertabrak oleh masuknya pengguna.
[ResponseCache(VaryByHeader = "User-Agent", Duration = 3*60*60)] public class SongModel: PageModel { β¦ }
Mengembalikan lagu-lagu populer
Sebagian besar lagu yang dihasilkan oleh GPT-2 yang disetel dengan baik dengan cara itu cukup membosankan, jika tidak sederhana. Untuk membuat klik pada "Jadikan Secara Acak" lebih menarik, saya menambahkan probabilitas 25%, bahwa alih-alih lagu yang benar-benar acak, Anda akan mendapatkan beberapa lagu, yang sebelumnya diputuskan oleh pengguna lain. Selain meningkatkan keterlibatan, ini meningkatkan peluang, bahwa Anda akan meminta lagu, disimpan dalam CDN, atau dalam memori.
Semua trik di atas dihubungkan bersama menggunakan injeksi dependensi ASP.NET di kelas Startup .
Voting
Tidak banyak yang istimewa tentang implementasi pemungutan suara. Ada SongVoteCache , yang membuat hitungan terus up to date. Dan iframe yang menampung tombol pilih pada halaman lagu, yang memungkinkan bagian penting dari halaman - judul dan lirik di-cache, sementara jumlah suara dan status login dimuat kemudian.
Hasil akhirnya

Versi demo yang berjalan di nettop saya, digawangi oleh CloudFlare (berikan sedikit kelonggaran, Core i3-nya) sekarang dibekukan dan dipindahkan ke tingkat gratis Azure App Service.
Repositori GitHub , berisi kode sumber, dan instruksi untuk menjalankan situs web dan menyetel model.
Rencana untuk masa depan / latihan
Hasilkan judul
GPT-2 sangat mudah disempurnakan. Orang bisa membuatnya menghasilkan judul lagu dengan mendahului atau suffixing setiap sampel lirik dari dataset dengan token buatan seperti <| startoftitle |> , diikuti oleh judul dari dataset yang sama.
Sebagai alternatif, pengguna dapat diizinkan untuk menyarankan dan / atau memilih judul.
Hasilkan musik
Setengah jalan melalui pengembangan Billion Songs, saya pikir akan lebih baik untuk mengunduh banyak file MIDI (itu adalah format musik jadul, yang jauh lebih dekat dengan teks, daripada mp3), dan melatih GPT-2 untuk menghasilkan lebih banyak . Beberapa file bahkan memiliki teks tertanam, sehingga Anda bisa mendapatkan generasi karaoke .
Saya tahu generasi musik dengan cara ini sangat mungkin, karena kemarin OpenAI benar-benar telah menerbitkan implementasi dari ide itu di blog mereka . Tapi, hore, mereka tidak melakukan karaoke! Saya menemukan, bahwa adalah mungkin untuk mengikis http://www.midi-karaoke.info untuk tujuan itu.
Gradient alias TensorFlow untuk .NET
Silakan, lihat
blog kami untuk setiap pembaruan.