Text Generation using minGPT and fast.ai
Published:
Andrej Karpathy, Tesla’s AI Director released minGPT, a mini version to OpenAI’s GPT. Normally a GPT would have billions of parameters and would take hours to train. Karpathy’s approach is to provide a smaller version of GPT, hence the name minGPT.
minGPT + fast.ai
Fast.ai has just released its version 2.0. This version is a total rewrite to its precursor. It works with other various PyTorch libraries and could also integrate with purely PyTorch code. Morgan Mcguire (morganmcg1 on Github) shared a code whereby the author incorporated Karpathy’s minGPT with fast.ai. It is from Mcguire’s code from which this project works upon. Credits to Morgan Mcguire for the code. I do not own the code, I simply changed minor bits (data, hyperparameters) in the overall code.
Yabes Elia & Zilbest
Yabes Elia is an editor for esports article. He was and is my current editor. Before that, he used to blog in his own page, Zilbest.com. The blog focused on several topics, including Philosophy, Romance, and Psychology. After reading his blog posts, I got the idea to train a language model upon his writing. I thought it would be interesting to let a deep learning model learn a person’s style of language. Credits to mas Yabes Elia, for allowing me to use his blog post at Zilbest.com as data source.
Code
The following code is based on morganmcg1’s “A Quick Demo of Andrej Karpathy’s minGPT Play Char Demo”. Only fragments of the important blocks of code were included.
Loading Data
The data is simply a .txt file filled with Yabes Elia’s articles on Zilbest. I’ve uploaded the .txt file to my Google Drive, loaded it, and showed the first 100 items.
raw_text = open(drive_path/'yabes-elia.txt', 'r').read()
raw_text[0:100]
'“You will never be happy if you continue to search for what happiness consists of. You will never li'
len(raw_text)
227914
Transforms
class CharTransform(Transform):
def __init__(self, data, block_size):
chars = list(set(data))
data_size, vocab_size = len(data), len(chars)
print('data has %d characters, %d unique.' % (data_size, vocab_size))
self.stoi = { ch:i for i,ch in enumerate(chars) }
self.itos = { i:ch for i,ch in enumerate(chars) }
self.block_size = block_size
self.vocab_size = vocab_size
self.data = data
self.n_sequences = math.ceil(len(self.data) / (self.block_size + 1))
def encodes(self, o):
i = np.random.randint(0, len(self.data) - (self.block_size + 1))
chunk = self.data[i:i+self.block_size+1]
dix = [self.stoi[s] for s in chunk]
return torch.tensor(dix)
def decodes(self, o):
t = ''.join([self.itos[s.item()] for s in o])
return TitledStr(t)
sl = 128
block_size = sl
n_samples = math.ceil(len(raw_text) / (block_size + 1))
tls = TfmdLists(list(range(n_samples)), tfms=[CharTransform(raw_text, 128)], split_idx=0, dl_type=LMDataLoader)
data has 227914 characters, 93 unique.
show_at(tls.train, 0)
Faktanya, mengubah sejarah dunia itu tidak akan pernah semudah membalikkan telapak tangan, atau dalam hal ini, menuliskan komenta
bs = 256
dls = tls.dataloaders(bs=bs, seq_len=sl)
dls.show_batch(max_n=2)
text | text_ | |
---|---|---|
0 | ibadi? Well, saya orang praktis. Saat saya masih jadi Managing Editor PC Gamer Indonesia, saya tentu lebih pro dengan MOBA di PC | badi? Well, saya orang praktis. Saat saya masih jadi Managing Editor PC Gamer Indonesia, saya tentu lebih pro dengan MOBA di PC. |
1 | al tidur sianah yang bisa memberikan jawaban jujur tentang siapa kita, bukan kuis-kuis di dunia maya yang tidak jelas algoritman | l tidur sianah yang bisa memberikan jawaban jujur tentang siapa kita, bukan kuis-kuis di dunia maya yang tidak jelas algoritmany |
DropOuput Callback
Replacing fast.ai Learner’s self.learn.pred
by its first element.
class DropOutput(Callback):
def after_pred(self):
self.learn.pred = self.pred[0]
Model: minGPT
mconf = GPTConfig(dls.char_transform.vocab_size, sl, n_layer=8, n_head=8, n_embd=512)
model = GPT(mconf)
learn = Learner(dls, model, loss_func=CrossEntropyLossFlat(), opt_func=partial(Adam, sqr_mom=0.95, wd=0.1),
cbs=[DropOutput])
Training Model
As per fast.ai practice, we let the Learner find the ideal Learning Rate, in our case we got about $0.003 \approx 3e-3$.
learn.lr_find()
/usr/local/lib/python3.6/dist-packages/fastprogress/fastprogress.py:74: UserWarning: Your generator is empty.
warn("Your generator is empty.")
SuggestedLRs(lr_min=0.0033113110810518267, lr_steep=2.0892961401841603e-05)
With that, we proceeded to training the model for 100 epochs and the LR which we’ve found optimal.
learn.fit_one_cycle(100, 3e-3)
epoch | train_loss | valid_loss | time |
---|---|---|---|
0 | 3.254104 | None | 00:14 |
1 | 3.155325 | None | 00:15 |
2 | 3.099205 | None | 00:15 |
3 | 3.032215 | None | 00:14 |
4 | 2.936109 | None | 00:14 |
5 | 2.849230 | None | 00:14 |
6 | 2.779433 | None | 00:14 |
7 | 2.719887 | None | 00:14 |
8 | 2.667851 | None | 00:14 |
9 | 2.624543 | None | 00:14 |
10 | 2.585148 | None | 00:14 |
11 | 2.552182 | None | 00:14 |
12 | 2.523161 | None | 00:14 |
13 | 2.498031 | None | 00:14 |
14 | 2.476660 | None | 00:14 |
15 | 2.455955 | None | 00:14 |
16 | 2.441366 | None | 00:14 |
17 | 2.427677 | None | 00:14 |
18 | 2.414104 | None | 00:14 |
19 | 2.397461 | None | 00:14 |
20 | 2.383328 | None | 00:14 |
21 | 2.368615 | None | 00:14 |
22 | 2.352587 | None | 00:14 |
23 | 2.335341 | None | 00:14 |
24 | 2.323342 | None | 00:14 |
25 | 2.305508 | None | 00:14 |
26 | 2.286461 | None | 00:14 |
27 | 2.262887 | None | 00:14 |
28 | 2.237531 | None | 00:14 |
29 | 2.211838 | None | 00:14 |
30 | 2.186196 | None | 00:14 |
31 | 2.156658 | None | 00:14 |
32 | 2.128527 | None | 00:14 |
33 | 2.104312 | None | 00:14 |
34 | 2.074495 | None | 00:14 |
35 | 2.046017 | None | 00:14 |
36 | 2.018104 | None | 00:14 |
37 | 1.990814 | None | 00:14 |
38 | 1.963953 | None | 00:14 |
39 | 1.938050 | None | 00:14 |
40 | 1.910195 | None | 00:14 |
41 | 1.882767 | None | 00:14 |
42 | 1.859885 | None | 00:14 |
43 | 1.833001 | None | 00:14 |
44 | 1.805273 | None | 00:14 |
45 | 1.777778 | None | 00:14 |
46 | 1.749810 | None | 00:14 |
47 | 1.721224 | None | 00:14 |
48 | 1.694282 | None | 00:14 |
49 | 1.668665 | None | 00:14 |
50 | 1.641540 | None | 00:14 |
51 | 1.614098 | None | 00:14 |
52 | 1.587708 | None | 00:14 |
53 | 1.560743 | None | 00:14 |
54 | 1.534708 | None | 00:14 |
55 | 1.510127 | None | 00:14 |
56 | 1.486278 | None | 00:14 |
57 | 1.461563 | None | 00:14 |
58 | 1.438166 | None | 00:14 |
59 | 1.415540 | None | 00:14 |
60 | 1.392969 | None | 00:14 |
61 | 1.371182 | None | 00:14 |
62 | 1.351205 | None | 00:14 |
63 | 1.331026 | None | 00:14 |
64 | 1.311882 | None | 00:14 |
65 | 1.293381 | None | 00:14 |
66 | 1.274096 | None | 00:14 |
67 | 1.256531 | None | 00:14 |
68 | 1.237806 | None | 00:14 |
69 | 1.221424 | None | 00:14 |
70 | 1.204520 | None | 00:14 |
71 | 1.189105 | None | 00:14 |
72 | 1.172827 | None | 00:14 |
73 | 1.156720 | None | 00:14 |
74 | 1.140753 | None | 00:14 |
75 | 1.125648 | None | 00:14 |
76 | 1.111875 | None | 00:14 |
77 | 1.097298 | None | 00:14 |
78 | 1.083305 | None | 00:14 |
79 | 1.069097 | None | 00:14 |
80 | 1.056546 | None | 00:14 |
81 | 1.044658 | None | 00:14 |
82 | 1.033119 | None | 00:14 |
83 | 1.021210 | None | 00:14 |
84 | 1.009997 | None | 00:14 |
85 | 0.999994 | None | 00:14 |
86 | 0.989661 | None | 00:14 |
87 | 0.979982 | None | 00:14 |
88 | 0.970661 | None | 00:14 |
89 | 0.961383 | None | 00:14 |
90 | 0.953398 | None | 00:14 |
91 | 0.946190 | None | 00:14 |
92 | 0.939140 | None | 00:14 |
93 | 0.932855 | None | 00:14 |
94 | 0.926477 | None | 00:14 |
95 | 0.921115 | None | 00:14 |
96 | 0.915792 | None | 00:14 |
97 | 0.911426 | None | 00:14 |
98 | 0.907237 | None | 00:14 |
99 | 0.904241 | None | 00:14 |
/usr/local/lib/python3.6/dist-packages/fastprogress/fastprogress.py:74: UserWarning: Your generator is empty.
warn("Your generator is empty.")
learn.recorder.plot_loss()
Testing Model
After training, we can feed the model a contextual phrase/sentence and let it generate the rest of the text. We sampled the model’s result and let it predict the next 2000 steps.
from minGPT.mingpt.utils import sample
Context 1: “Karena itu,”
In English: “Therefore,”.
from minGPT.mingpt.utils import sample
context = "Karena itu,"
x = torch.tensor([dls.char_transform.stoi[s] for s in context], dtype=torch.long)[None,...].to(dls.device)
y = sample(model, x, 2000, temperature=0.9, sample=True, top_k=5)[0]
completion = ''.join([dls.char_transform.itos[int(i)] for i in y])
print(completion)
Karena itu, sistem gurus muluk disemungkinan di sini adalah kemuncegan banyak karakteristik pahlawan saya yang berbeda atas hidup kita bisa mendapatkan kehidupan hasil, keturunan, ataupun hilang rasa, satu hal yang bisa dijelaskan dengan kata-kata Anda tadi, kemungkinan besar, Anda terkritik dengan sendiri. Jika Anda tidak tahu kalah itu jauh lebih sulit dan lebih tertarik meraih pada konfirmasi dengan keatan kita, ataupun hal-hal lainnya karena sebenarnya ada satu hal yang menulis.
Saya pribadi, jika saya sangat merasa menyenangkan untuk komenangkap ini mungkin bisa berpikir diterakhir yang membutuhkan bahwa pribadi jika Anda tidak ada yang suka daripada satu kawan saya sudah berpasangan dalam menghubungan sesuatu Anda tidak suka dengan sekalipun, kategori saya yakin Anda juga masih sering berpikir saya bisa memaknakan para pembela atau malah sarat dengan personal tentang skeptisisme.
Saya juga tidak akan berubah waktu.
Namun, kebalikan dari kreatif seperti kita drumah, dan perspektif seorang istri ini.
Misalnya saja seperti ini, saya tidak pernah menghantarkan penutup artikel ini. Setidaknya, saya memang sudah bekerja dari sudut pandang menghasilkan kebetulanan saja, selanjutnya seperti ini. Satu hal yang membuat saya pernah menuliskan saya bekerja keras dan kembali buku sosial dan sebagai kuburan tahun berapa buku saya adalah sebagian besar tadi sebenarnya sudah tidur dari pasangan.
Di dunia riil, saya pribadi memiliki alam hidup yang berbeda dari segi yang saya pernah berada di depan kita, dan keluarga kita mau menikmati keseluarga ketimbang dua memahami segera pandai bagaimana relevan.
Dari sejumlah satu komunitas adalah seperti grafis di bawah ini untuk diri sendiri. Misalnya, satu hal yang sama-sama sekali, dan kuis-kuis di jaman sekarang ini, ada banyak h kawan-kawan saya yang memegang tidak berbagai semua saya kira semua pasti tidak menyebutkan berakhir demikian? Kita juga tidak akan lelahnya selalu berada dengan satu cara yang sama (saya). Namun juga saya tahu
Context 2: “Filosofi saya adalah”
In English: “My philosophy is”.
context = "Filosofi saya adalah"
x = torch.tensor([dls.char_transform.stoi[s] for s in context], dtype=torch.long)[None,...].to(dls.device)
y = sample(model, x, 2000, temperature=0.9, sample=True, top_k=5)[0]
completion = ''.join([dls.char_transform.itos[int(i)] for i in y])
print(completion)
Filosofi saya adalah ketidakpastian dan sang pernah berhasil mengolahnya kebanyakan soal image yang mengatakan bahwa saya menghabiskan waktu untuk berubah dambaan hati Anda, dan sebelum tulisan ini juga sangat baik itu memperti sebelumnya.
Saya kira semua suka saya dibelikan banyak mobile.
Ditambah lagi, atau proses berpikir lebih jauh masing-masing. Pasalnya, merasa tidak memuaskan keras untuk memperkayakan diri dengan kepentingan yang saya tadi, seperti pemilik solusi yang lebih beruntung ketimbang mendengarkan gilita semua itu tidak aktif dan marah terhadap kebebahagiaan di kondisi lainnya sebelumnya.
Akhirnya, saya pribadi juga melihat ketika mana yang semua hal yang bisa Anda tidak akan mengeluhi kegagalanan, saya kira semua bisa sampai ke titik ini – tulisan saya ditujukan di sini adalah kesatuan yang bisa kita ahadapi di kepentingan industri ini membutuhkan sebagian tadi berpikir – karena mencerita jadi sebuah pasangan atau berpikir lebih jauh.
Maksud saya terhasal menghadapi sesuai dengan keputusan yang saya rasakan. Setiap kita pasti punya keinginan bisa jadi profesional, berpikir berbasiskan bisa jadi salah satu cenderung untuk mencari tahu alias karena sifat kepintaran tersebut di sana.
Misalnya, terasal dari satu tim/ gumen, pasti pacaran tadi pacarnya, pernah saya bisa mengajak keluarga dalam membela kerap bersisa dapat membebaskan apa yang kita percayai adalah kesuksesan dan berbagi berpikir internet dan menggelitik. Tutup jawaban memang mudah mendorong untuk mencari kesadar dan satu pasangan Anda, selama 15 tahun yang berbeda dari kondisi yang lainnya.
Namun demikian, kecenderungan untuk melarang lebih besar ketimbang harus kita sedih memiliki personalitas kita bisa jadi tolak ukur dan kepintaran seseorang seperti seperti bahkan sebuah soal game, seperti seperti yang seperti apakah yang bisa semesta dan mengurus rumah tangga.
Saya adalah rekaman sepenuhnya dengan tangan Anda, Anda akan pernah mendengar adalah orang-orang yang berpikiran – siedak akan berarti saya
Context 3: “Bagi saya, hidup”
In English: “For me, life”.
context = "Bagi saya, hidup"
x = torch.tensor([dls.char_transform.stoi[s] for s in context], dtype=torch.long)[None,...].to(dls.device)
y = sample(model, x, 2000, temperature=0.9, sample=True, top_k=5)[0]
completion = ''.join([dls.char_transform.itos[int(i)] for i in y])
print(completion)
Bagi saya, hidup itu sebenarnya manusia itu bisa berubah-ubah dan kesamaan Anda…
So, saya pribadi mencari saya, kemungkinan besar, Anda juga akan memuaskan keruntungan keinginan sosial dan memproses bebagai sebuah kehidupannya seperti karena pil berargumen bahwa pada pengecualian. Jika Anda bisa membaca itu saja yang sebenarnya tak punya kesedihan menuntut dunia. Dari perspektif seorang berbeda jadi berusaha dengan masalah dunia nyata, termasuk sebagian dan berbeda. Sebelum kita, meminilah kepuasannya sesama seperti ini adalah satu hal yang pasti pernah merasakan hal yang sama, termasuk dalam hidup itu terjadi.
Siapakah yang saya tidak mengalahkan pusat ini seringkali disadari. Dalam perspektif yang membuat Anda pernah dengan percaya setiap orang idealisme itu biasanya merupakannya.
Namun setiap kita punya keingintahuan yang sama sama seperti ini, saya percaya bahwa adalah rekan besar tadi setiap orang yang paling suka merasa melihat hal tersebut tertarik untuk menjadi bagian dari kebencian Andscommbias yang bernada dari segi sebenarnya bisa berkurang terus bekerja atau bisa memiliki cerita semakin banyak orang suami itu terjadi ketika kita masih mencari kesalahan prestasi atau tidak berawal dengan argumen yang namanya pendapat yang berbeda, dengan sedikit juga akan lakukan dari segudang seksual. Tokoh karena kita punya sudah berpasangan tahun lalu bagaimana jika kita berada di misalnya. Namun, kenyataannya, banyak orang tua, anak ‘multiplaya yang digunakan oleh orang lain – meski tidak ada yang lainnya.
Sebenarnya apa? Kegalauan, keyakinan Anda tujuan selalu mengerti pasangan Anda selalu merasa saat mengakui sesuatu di saat Anda.
Memang, saya sudah menyarankan pertama atau sistem berbeda di sini saat ini.
Akhirnya, tidak sama seperti yang saya rasakan. Saya kira saya tahu bahwa kita bisa saja memiliki hasrat tersebut karena tidak akan pernah terlalu memang negatif lainnya.
Misalnya saja seperti ini, saya kira saya juga tidak mau berhadapan dengan soal lainnya.
Sayangnya, m
Closing Remarks
Conclusion
To sum up, here are several of my remarks:
- McGuire showed how easy it is to integrate fast.ai with PyTorch models and libraries.
- Fast.ai abstracts the need to dive into repetitive task of creating a Trainer for the model, learning rate scheduling, etc.
- Karpathy’s minGPT is very versatile. Despite having much less parameters to OpenAI’s GPT, it still showed good results.
- Although some of the sentences pretty much didn’t have proper grammar, it’s still interesting to let the model write text in the style of mas Yabes Elia.
I’ve learned a lot by simply modifying McGuire’s code. As a novice in DL, Language Modelling is certainly something new for me. I’m excited to see what DL is capable of doing across applications. I hope you’ve learned something like I did!
Credits
- morganmcg1’s A Quick Demo of Andrej Karpathy’s minGPT Play Char Demo.
- karpathy’s minGPT.
- Yabes Elia’s Zilbest blog posts.