ავტორები: ლუკა დარსალია, ნიკა საგინაძე, ნიკა მიქაბერიძე
Ჩვენი პროექტი ემსახურება ინგლისურენოვანი რეზიუმეების კლასიფიკაციას. Ეს კლასიფიკაცია არის მეტწილად ზოგადი და არ არის დაფუძნებული ძალიან კომპლექსური მოდელების არქიტექტურებზე, ძირითადად არის გამოყენებული სხვადასხვა კლასიკური ML-ის მოდელები (SVM, RF, LogisticRegression, KNN) და ერთხელ არის გამოყენებული Transformer-ი. Რა თქმა უნდა, შეიძლებოდა ეს ყოფილიყო უფრო დეტალური, თუმცა ამას უფრო მეტი დატაც და უფრო მეტი “compute”-იც დასჭირდებოდა, რომ ტრანსფორმერს გადაემუშავებინა ეს დატა. Მოკლედ რომ ვთქვათ, ჩვენი პროექტის მიზანი არის ამოიცნოს რეზიუმე მაგალითდ პითონის დეველოპერისაა, თუ ხელოსნის. Თუმცა, თუ დავადგინეთ, რომ პითონის დეველოპერისაა, ჩვენი მოდელი ვერ დააბრუნებს მის ზუსტ თანამდებობას, “სენიორია” იგი, მიდლი თუ ჯუნიორი. Ეს მეტწილად ზოგადი იქნება.
Პირველ რიგში, უნდა ვისაუბროთ დატაზე. Დატის ნაწილი წამოვიღეთ კეგლიდან, ნაწილი კი huggingface-ის ერთ-ერთი დატასეტია. Უმეტესი, მაინც huggingface-საა. Დატის ნაწილს რაც კიდევ შეეხება, შეგვიძლია ვნახოთ EDA.ipynb ნოუთბუქში. Ნოუთბუქში დეტალურად არის განხილული თუ რა ტიპის დატა არის, რა კლასებია, როგორი ტექსტები, ასევე გვიწერია “წმენდის” ფუნქციები. Შეიძლება ითქვას, რომ ეს ნოუთბუქი აგებს დატის სიწმინდეზე და “შეცნობაზე” პასუხს, არის რამდენიმე გრაფიკი რომელიც გვეხმარება სტრუქტურის გააზრებაში და სხვა.
TF-IDF (Term Frequency - Inverse Document Frequency) Ჩვენი პირველი იდეა იყო ტექსტებიდან TF-IDF-ით წონების დაგენერირება. TF-IDF არის ძალიან სტატისტიკური მოდელი, რომელიც ძირითადად მუშაობს კარგად პატარა კორპუსზე, თუმცა გვინდოდა აქაც გაგვეტესტა. TF-IDF-ი ნულიდანაც დავწერეთ, თუმცა გაშვება რომ ვცადეთ 12 საათი დაგვიწერა, ამიტომ ისევ sklearn-ის პექეჯი გამოვიყენეთ. Როგორც მოგეხსნებათ, ეს მოდელი აბრუნებს წონებს, რომლების ზომაც არის მთელი vocabulary-ის ზომის ხელა, vocabulary კი საკმაოდ დიდი იყო, 118000 დაახლოებით, რაც შემდგომ მოდელების წვრთნაში ხელს შეგვიშლიდა. Ამიტომ, გადავწყვიტეთ მოგვედო ამ ვექტორებისთვის SVD ფაქტორიზაცია და დაგვეყვანა ეს ვექტორები არა 1200 განზომილებაზე და შემდგომ გაგვეწვრთნა ეს მოდელები. Უფრო დეტალურ შედეგებს TF-IDF.ipynb ნოუთბუქშიც ნახავთ, თუმცა რა თქმა უნდა აქაც გაგანდობთ ყველაფერს.
- KNN - 0.68
- LR - 0.78
- RF - 0.73
- SVM - 0.75 Ეს იყო შედეგები, რომლებიც დაგვიბრუნეს მოდელებმა ტესტ სეტებზე. Რა თქმა უნდა, ეს არ ვიმყოფინეთ და ვცადეთ ჩვენს სივიებზე და არამარტო ჩვენს სივიებზე, ერთ-ერთ ჯიპიტის დაგენერირებულ სივიზეც, რომელსაც იხილავთ ნოუთბუქში. Საბოლოოდ, დასკვნა იყო ის, რომ უკეთესი/სხვა მიდგომებიც არსებობს, ვიდრე უბრალოდ სტატისტიკურად TF-IDF-ით გაკეთება.
GLoVE არის ერთ-ერთი ყველაზე ძველი მოდელის არქიტექტურა NLP-ში. Იდეა არის საკმაოდ მარტივი: მოდელი ითვალისწინებს გვერდიგვერდ მდგომი სიტყვების სიხშირეებს კონკრეტული სიტყვის წონის დაგენერირებაში. Უფრო ღრმად რომ ჩავიდეთ, საკმაოდ შორს წავალთ, ამიტომ თუ დაგაინტერესათ შეგიძლიათ წაიკითხოთ. Იდეა და შესრულება ზუსტად იგივე იყო რაც TF-IDF-ში.
- დავწეროთ მოდელი from scratch.
- დავატრენინგოთ მოდელი ჩვენს კორპუსზე, მივიღოთ წონები.
- შევუშვათ ეს წონები იმ 4 მოდელში, რომელშიც შევუშვით TF-IDF-ის წონები.
Დავიწყოთ მიმოხილვა. From scratch დაწერის შემდეგ, ენის მოდელი გაიწვრთნა, მივიღეთ წონები, თუმცა პრობლემა გაჩნდა იმ 4 მოდელის გაწვრთნის შემდეგ. Როგორც ჩანს, 12_000 დატა არ არის საკმარისი ისეთი მოდელის მისაღებად, რომ წესიერი შედეგები მივიღოთ არცერთი ტესტ სეტის ექიურესი არ ცდებოდა 0.5-ს, რაც ძალიან ცუდი შედეგია, ამიტომ შეგვიძლია დავასკვნათ, რომ ნულიდან გლოვის გაწვრთნა აბსოლუტური სისულელე იყო. Თუმცა, ჩვენ ამაზე არ დავნებდით და ავიღეთ pretrained glove-ის მოდელი, რომელზეც მერე გავწვრთენით ეს 4 მოდელი და ამ შემთხვევაში მოდელებმა არ დააბრუნეს ის სასირცხვილო შედეგები, რომლებიც დააბრუნეს პირველ შემთხვევაში. Შედეგები ტრიალებდნენ 0.6-სა და 0.7-ს შორის. Უფრო მეტი დეტალი შეგიძლიათ ნახოთ glove_classification.ipynb ნოუთბუქში, სადაც ძალიან დეტალურად და კარგად არის აღწერილი ყველა ოპერაცია რაც შევასრულეთ.
Რობერტა არის ცნობილი ენის მოდელი, ენკოდერი, რომელიც იდეალურია ასეთი downstream ტასკებისთვის, არის საკმაოდ პატარა, თუმცა მიუხედავადა მისა მაინც სჭირდება GPU ქომფიუთი. Ამისდა მიუხედავად, მაინც გავწვრთენით ჩვენთვის საჭირო ტასკზე და ამის დეტალურ კოდს და აღწერას ნახავთ Roberta_training_classification.ipynb ნოუთბუქში. Მთავარი catch რა იყო აქ: ზოგადად, პატარა ენის მოდელები ვერ ამუღამებენ დიდ კონტექსტს, არ აქვთ დიდი “მეხსიერება”. Ანუ, თუ ვეტყვი 1500 სიტყვას, იგი მხოლოდ პირველ 500-ს დაიმახსოვრებს, თუ კონტექსტ ვინდოუ არის 500. Ამის მიუხედავად, ღირდა ცდად, რადგან ჩვენი დატის უმეტესობა მოქცეული იყო 1000 სიტყვამდე, რაც დაახლოებით 750 ტოკენია, და ამ 750 ტოკენიდან ვგონებ პირველ 512ში მოექცეოდა ის საჭირო ინფორმაცია, რომელიც საჭიროა კლასიფიკაციისთვის. Მქონდა კიდევ ერთი იდეა, რომელიც ვერ განვახორციელე იმის გამო, რომ დამიმთავრდა კოლაბზე compute unit-ები :დ. Იდეა იყო ასეთი: რადგან ინფუთი შედარებით დიდი იყო, გამეკეთებინა sliding window და ტრენინგი დამეწერა ლუპში და არა trainer API-თ. Ახლა როგორ იმუშავებდა ჩემი ტრენინგი:
for epoch in range(5): # num_train_epochs
model.train()
for step, batch in enumerate(train_loader):
batch = {k: v.to(device) for k, v in batch.items()}
###Აქ ბათჩი იქნებოდა sliding window-თი გადაყოლილი ერთი დოკუმენტი. Მაგალითად, თუ იყო 1000 სიტყვა, კონტექსტი მექნებოდა 500, ვინდოუ კი მექნებოდა 250, მექნებოდა დაახლოებით ასეთი საბოლოო ბეთჩი:[[0:500],[250:750],[500:1000]].
###Ახლა, აქ რა არის catch: მე აუთფუთების დათვლის შემდეგ დამიბრუნდებოდა ლოგიტები, ანუ ალბათობები და შემიძლია დავთვალო ლოგიკურად მოდელის “confidence” მაგ კონკრეტულ აუთფუთზე. Ამის შემდეგ, ბეთჩიდან რომელზეც დამიბრუნდებოდა აუთფუთი ყველაზე დიდი ქონფიდენსით, ჩავთვლიდი, რომ ეგ არის ჩემი ნამდვილი აუთფუთი და მხოლოდ ამის შემდგომ ვიზამდი loss.backward-ს.
outputs = model(**batch)
for i in outputs:
Max_confidence_outputs = compute_max_conf(outputs) # pseudocode
loss = Max_confidence_outputs.loss
loss.backward() optimizer.step() scheduler.step() optimizer.zero_grad()
Სამწუხაროდ ეს ვერ მოვახერხეთ მაგრამ აუცილებლად ვიზამთ როცა მოგვეცემა დიდ ტექსტებთან მუშაობის საშუალება და compute რომელშიც ფულის ხდა ჩვენ არ მომიწევს.
Დავუბრუნდეთ უკვე გაკეთებულ საქმეს. Ჩვენი მოდელის პერფომანსი ბევრად მაღალი იყო ამ შემთხვევაში და შეადგენდა 0.91-ს accuracy score-ით დათვლის შედეგად. Შემდეგ გავტესტეთ იმ სივიებზე, რომლებზეც TF-IDF-იც გავტესტე და ცხადია, უკეთესი შედეგიც დამიბრუნა. Უფრო დეტალურად იხილეთ ნოუთბუქში.
Და აი, მივედით საბოლოო “იდეამდე”. Sentence-transformers არის ფაქტობრივად კარგი ტექსტ ემბედინგების მქონე მოდელების არქიტექტურა, საიდანაც ჩვენ წამოვიღეთ ერთ-ერთი 22 მილიონიანი მოდელი (საკმაოდ პატარაა სენთენს ტრანსფორმერისთვის) და დავაგენერირეთ წონები აქაც, წინა მეთოდების მსგავსად. Ამ წონების შემდეგ გადავწყვიტეთ კვლავ გაგვეწვრთნა ის 4 მოდელი და დაგვედარებინა შედეგები GLoVE-თან, TF-IDF-თან და ჩვენს გაწვრთნილ bert-თან. Გადავიდეთ პირდაპირ დასვკნაზე, რადგან sampling-ის კოდს იხილავთ sentence-transformers.ipynb ნოუთბუქში. Საუკეთესო შედეგი რაც ამან დადო იყო 0,73. Საბოლოოდ, შედეგები ტრიალებდნენ 0.67-სა და 0.73-ის ფარგლებში, რაც ცხადია ჯობდა GLoVE-ს.
Ამ პროექტიდან რამდენიმე დასკვნის გამოტანა შემიძლია. Ისეთი კლასიფიკაციის ტაკსებისთვის, სადაც კონკრეტულმა სიტყვამ შეიძლება ყველაფერი გადაწონოს, TF-IDF ზედგამოჭრილია და ზოგავს ძალიან ბევრ compute resource-ს. Თუ მაინც და მაინც ჩემთვის სიზუსტეა პრიორიტეტული, აუცილებელია რომ ან სენთენს ტრანსფორმერი ან ტრანსფორმერი გავწვრთნა ჩემს downstream task-ზე, თუმცა მაინც წაიღებს ძალიან ბევრ რესურსს. GLoVE საკმაოდ ცუდი იდეა იყო ამ შემთხვევაში, ძალიან ცუდი შედეგები დააბრუნა სხვა მოდელებთან შედარებით, მიუხედავად იმისა რომ TF-IDF-იც სტატისტიკური მოდელია, მაინც ბევრად უკეთესი იყო.
Საბოლოოდ, დავრწმუნდი, რომ მარტივი ML-ის მოდელი და ორიგინალური მიდგომა წონების დაგენერირების მხრივ საკმაოდ საკმარისი შეიძლება აღმოჩნდეს კლასიფიკაციის ტასკისთვის და სულ არ იყოს შეიძლება საჭირო transformer-ისნაირი დიდი და მძლავრი არქიტექტურის გამოყენება. Მოკლედ რომ ვთქვათ: NLP-ის ამოცანები მარტო ნეირონული ქსელებით არ იხსნება და აქვს ბევრად უფრო მაგარი და ორიგინალური ამოხსნებიც! :დ