Entri
Natural Language Processing (NLP) adalah bidang pembelajaran mesin yang populer dan penting. Di hub ini, saya akan menjelaskan proyek pertama saya yang terkait dengan analisis pewarnaan emosional ulasan film yang ditulis dengan Python. Tugas analisis sentimental cukup umum di antara mereka yang ingin menguasai konsep dasar NLP, dan dapat menjadi analog dari 'Hello world' di bidang ini.
Pada artikel ini, kita akan melalui semua tahapan utama proses Ilmu Data: mulai dari membuat dataset Anda sendiri, memprosesnya dan mengekstraksi fitur menggunakan perpustakaan NLTK, dan akhirnya mempelajari dan menyetel model menggunakan scikit-learning. Tugas itu sendiri adalah untuk mengklasifikasikan ulasan menjadi tiga kelas: negatif, netral dan positif.
Formasi Data Corpus
Untuk mengatasi masalah ini, orang dapat menggunakan beberapa badan data yang sudah jadi dan beranotasi dengan ulasan dari IMDB, yang banyak terdapat di GitHub. Tetapi diputuskan untuk membuat ulasan Anda sendiri dalam bahasa Rusia yang diambil dari Kinopoisk. Agar tidak menyalinnya secara manual, kami akan menulis web parser. Saya akan menggunakan perpustakaan
permintaan untuk mengirim
permintaan http, dan
BeautifulSoup untuk memproses file html. Pertama, mari kita tentukan fungsi yang akan membawa tautan ke ulasan film dan mengambilnya. Agar Kinopoisk tidak mengenali bot di kami, Anda perlu menentukan argumen
header di fungsi
requests.get , yang akan mensimulasikan browser. Penting untuk memasukkan kamus ke dalamnya dengan tombol User-Agent, Accept-language dan Accept, yang nilainya dapat ditemukan di alat pengembang browser. Selanjutnya, pengurai dibuat dan ulasan diambil dari halaman, yang disimpan dalam kelas markup html _reachbanner_.
import requests from bs4 import BeautifulSoup import numpy as np import time import os def load_data(url): r = requests.get(url, headers = headers)
Kami menyingkirkan markah html, namun, ulasan kami masih berupa objek
BeautifulSoup , tetapi kami harus mengubahnya menjadi string. Fungsi
konversi tidak hanya itu. Kami juga akan menulis fungsi yang mengambil nama film, yang nantinya akan digunakan untuk menyimpan ulasan.
def convert(reviews):
Fungsi parser terakhir akan mengambil tautan ke halaman utama film, kelas ulasan dan cara untuk menyimpan ulasan. Fungsi ini juga mendefinisikan
penundaan antara permintaan yang diperlukan untuk menghindari larangan. Fungsi ini berisi loop yang mengambil dan menyimpan ulasan mulai dari halaman pertama, sampai bertemu dengan halaman yang tidak ada di mana fungsi
load_data akan mengekstrak daftar kosong dan loop akan rusak.
def parsing(url, status, path): page = 1 delays = [11, 12, 13, 11.5, 12.5, 13.5, 11.2, 12.3, 11.8] name = get_name(url) time.sleep(np.random.choice(delays))
Kemudian, menggunakan siklus berikut, Anda dapat mengekstrak ulasan dari film yang ada di daftar
urles . Daftar film perlu dibuat secara manual. Mungkin saja, misalnya, untuk mendapatkan daftar tautan ke film dengan menulis fungsi yang akan mengekstraknya dari 250 film teratas dari pencarian film, sehingga tidak melakukannya secara manual, tetapi 15-20 film akan cukup untuk membentuk dataset kecil dari seribu ulasan untuk setiap kelas. Juga, jika Anda mendapatkan larangan, program akan menampilkan film dan kelas mana parser berhenti untuk melanjutkan dari tempat yang sama setelah melewati larangan.
path =
Pretreatment
Setelah menulis parser, mengingat film acak untuknya dan beberapa larangan dari pencarian film, saya mencampur ulasan dalam folder dan memilih 900 ulasan dari setiap kelas untuk pelatihan dan sisanya untuk kelompok kontrol. Sekarang perlu untuk pra-proses perumahan, yaitu, tokenize dan menormalkannya. Tokenizing berarti memecah teks menjadi beberapa komponen, dalam hal ini menjadi kata-kata, karena kita akan menggunakan representasi sekumpulan kata. Dan normalisasi terdiri dari mengubah kata menjadi huruf kecil, menghilangkan kata-kata henti dan kebisingan berlebih, gagap, dan trik lainnya yang membantu mengurangi ruang tanda.
Kami mengimpor perpustakaan yang diperlukan.
Teks tersembunyi from nltk.corpus import PlaintextCorpusReader from nltk.stem.snowball import SnowballStemmer from nltk.probability import FreqDist from nltk.tokenize import RegexpTokenizer from nltk import bigrams from nltk import pos_tag from collections import OrderedDict from sklearn.metrics import classification_report, accuracy_score from sklearn.naive_bayes import MultinomialNB from sklearn.model_selection import GridSearchCV from sklearn.utils import shuffle from multiprocessing import Pool import numpy as np from scipy.sparse import csr_matrix
Kami mulai dengan mendefinisikan beberapa fungsi kecil untuk preprocessing teks. Yang pertama, disebut
lower_pos_tag, akan membawa daftar dengan kata-kata, mengonversikannya menjadi huruf kecil, dan menyimpan setiap token ke dalam tuple dengan bagian bicaranya. Operasi penambahan part of speech ke sebuah kata disebut penandaan Part of speech (POS) dan sering digunakan dalam NLP untuk mengekstrak entitas. Dalam kasus kami, kami akan menggunakan bagian-bagian ucapan dalam fungsi berikut untuk menyaring kata-kata.
def lower_pos_tag(words): lower_words = [] for i in words: lower_words.append(i.lower()) pos_words = pos_tag(lower_words, lang='rus') return pos_words
Teks-teks tersebut mengandung sejumlah besar kata-kata yang terlalu sering ditemukan sehingga tidak berguna untuk model (yang disebut kata-kata berhenti). Pada dasarnya, ini adalah preposisi, konjungsi, kata ganti yang dengannya tidak mungkin untuk menentukan yang merujuk kelas recall. Fungsi
bersih hanya menyisakan kata benda, kata sifat, kata kerja dan kata keterangan. Perhatikan bahwa itu menghilangkan bagian-bagian ucapan, karena mereka tidak diperlukan untuk model itu sendiri. Anda juga dapat memperhatikan bahwa fungsi ini menggunakan stamming, yang intinya adalah untuk menghilangkan sufiks dan awalan dari kata-kata. Ini memungkinkan Anda mengurangi dimensi tanda, karena kata-kata dengan genera dan huruf yang berbeda akan dikurangi menjadi token yang sama. Ada analog yang lebih kuat dari pengaplikasian - lemmatization, ini memungkinkan Anda untuk mengembalikan bentuk awal kata tersebut. Namun, ini bekerja lebih lambat daripada gagap, dan, di samping itu, NLTK tidak memiliki lemmatizer Rusia.
def clean(words): stemmer = SnowballStemmer("russian") cleaned_words = [] for i in words: if i[1] in ['S', 'A', 'V', 'ADV']: cleaned_words.append(stemmer.stem(i[0])) return cleaned_words
Selanjutnya, kita menulis fungsi final yang akan mengambil label kelas dan mengambil semua ulasan dengan kelas ini. Untuk membaca kasus ini, kita akan menggunakan metode
mentah objek
PlaintextCorpusReader , yang memungkinkan Anda untuk mengekstrak teks dari file yang ditentukan. Selanjutnya, tokenization digunakan RegexpTokenizer, bekerja berdasarkan ekspresi reguler. Selain kata-kata individual, saya menambahkan ke model bigrams, yang merupakan kombinasi dari semua kata tetangga. Fungsi ini juga menggunakan objek
FreqDist , yang mengembalikan frekuensi kemunculan kata. Ini digunakan di sini untuk menghapus kata-kata yang muncul di semua ulasan dari kelas tertentu hanya sekali (mereka juga disebut hapaks). Dengan demikian, fungsi akan mengembalikan kamus berisi dokumen yang disajikan sebagai sekumpulan kata dan daftar semua kata untuk kelas tertentu.
corpus_root =
Tahap pra-pemrosesan adalah yang terpanjang, jadi masuk akal untuk memparalelkan pemrosesan kasus kami. Ini dapat dilakukan dengan menggunakan modul
multiprosesing . Pada bagian selanjutnya dari kode program, saya memulai tiga proses yang secara bersamaan akan memproses tiga folder dengan kelas yang berbeda. Selanjutnya, hasilnya akan dikumpulkan dalam satu kamus. Preprocessing ini selesai.
if __name__ == '__main__': data = {} labels = ['neutral', 'bad', 'good'] p = Pool(3) result = p.map(process, labels) for i in result: data.update(i) p.close()
Vektorisasi
Setelah kami memproses kasus sebelumnya, kami memiliki kamus di mana untuk setiap label kelas berisi daftar dengan ulasan yang kami token, normalkan, dan diperkaya dengan bigrams, serta daftar kata-kata dari semua ulasan kelas ini. Karena model tidak dapat memahami bahasa alami seperti yang kita lakukan, tugasnya sekarang adalah menyajikan ulasan kami dalam bentuk angka. Untuk melakukan ini, kami akan membuat kosakata umum, yang terdiri dari token unik, dan dengan itu kami akan membuat vektor setiap ulasan.
Untuk mulai dengan, kami membuat daftar yang berisi ulasan dari semua kelas bersama dengan label mereka. Selanjutnya, kita membuat kosakata umum, mengambil dari setiap kelas 10.000 kata yang paling umum menggunakan metode
most_common dari
FreqDist yang sama. Sebagai hasilnya, saya mendapat kosakata yang terdiri dari sekitar 17.000 kata.
Ada beberapa cara untuk membuat vektor teks. Yang paling populer di antaranya: TF-IDF, pengkodean langsung dan frekuensi. Saya menggunakan pengkodean frekuensi, intinya adalah untuk menyajikan setiap ulasan sebagai vektor, unsur-unsurnya adalah jumlah kemunculan setiap kata dari kosa kata.
NLTK memiliki pengklasifikasi sendiri, Anda dapat menggunakannya, tetapi mereka bekerja lebih lambat daripada rekan-rekan mereka dari
scikit-learn dan memiliki lebih sedikit pengaturan. Di bawah ini adalah kode untuk pengkodean untuk
NLTK . Namun, saya akan menggunakan model Naive Bayes dari
scikit-belajar dan mengkodekan ulasan, menyimpan atribut dalam matriks jarang dari
SciPy , dan label kelas dalam array
NumPy yang terpisah.
Karena dalam dataset ulasan dengan tag tertentu berjalan satu demi satu, yaitu, pertama semua netral, lalu semua negatif dan seterusnya, Anda perlu mencampurnya. Untuk melakukan ini, Anda dapat menggunakan fungsi
acak dari
scikit-learn . Ini hanya cocok untuk situasi ketika tanda dan label kelas berada dalam array yang berbeda, karena memungkinkan Anda untuk mencampur dua array secara bersamaan.
Pelatihan model
Sekarang tinggal melatih model dan memeriksa akurasinya pada kelompok kontrol. Sebagai model, kita akan menggunakan model classifier Naive Bayes.
Scikit-belajar memiliki tiga model Naive Bayes tergantung pada distribusi data: biner, diskrit, dan kontinu. Karena distribusi fitur kami terpisah, kami memilih
MultinomialNB .
Pengklasifikasi Bayesian memiliki
parameter alfa hiper, yang bertanggung jawab untuk menghaluskan model. Naif Bayes menghitung probabilitas setiap ulasan milik semua kelas, untuk ini mengalikan probabilitas kondisional dari penampilan semua kata ulasan, asalkan mereka milik kelas tertentu. Tetapi jika beberapa kata ulasan tidak ditemukan dalam kumpulan data pelatihan, maka probabilitas kondisionalnya sama dengan nol, yang membatalkan kemungkinan bahwa ulasan tersebut milik kelas apa pun. Untuk menghindari ini, secara default, sebuah unit ditambahkan ke semua probabilitas kata bersyarat, mis.
Alpha sama dengan satu. Namun, nilai ini mungkin tidak optimal. Anda dapat mencoba memilih
alpha menggunakan pencarian kisi dan validasi silang.
parameter = [1, 0, 0.1, 0.01, 0.001, 0.0001] param_grid = {'alpha': parameter} grid_search = GridSearchCV(MultinomialNB(), param_grid, cv=5) grid_search.fit(X, Y) Alpha, best_score = grid_search.best_params_, grid_search.best_score_
Dalam kasus saya, kisi perapian memberikan nilai optimal dari hiperparameter sama dengan 0 dengan akurasi 0,965. Namun, nilai ini jelas tidak akan optimal untuk dataset kontrol, karena akan ada sejumlah besar kata yang belum ditemukan sebelumnya di set pelatihan. Untuk dataset referensi, model ini memiliki akurasi 0,598. Namun, jika Anda meningkatkan
alfa ke 0,1, keakuratan pada data pelatihan akan turun menjadi 0,82, dan pada data kontrol itu akan meningkat menjadi 0,62. Kemungkinan besar, pada kumpulan data yang lebih besar, perbedaannya akan lebih signifikan.
model = MultinomialNB(0.1) model.fit(X, Y)
Kesimpulan
Diasumsikan bahwa model tersebut harus digunakan untuk memprediksi ulasan yang kata-katanya tidak digunakan untuk membentuk kosa kata. Oleh karena itu, kualitas model dapat dievaluasi dengan keakuratannya pada bagian kontrol data, yaitu 0,62. Ini hampir dua kali lebih baik daripada hanya menebak, tetapi akurasinya masih sangat rendah.
Menurut laporan klasifikasi, jelas bahwa model melakukan yang terburuk dengan ulasan yang memiliki warna netral (akurasi 0,47 berbanding 0,68 untuk positif dan 0,76 untuk negatif). Memang, ulasan netral berisi kata-kata yang merupakan karakteristik dari ulasan positif dan negatif. Mungkin, keakuratan model dapat ditingkatkan dengan meningkatkan volume dataset, karena set data tiga ribu agak sederhana. Juga, akan mungkin untuk mengurangi masalah menjadi klasifikasi biner dari tinjauan menjadi positif dan negatif, yang juga akan meningkatkan akurasi.
Terima kasih sudah membaca.
PS Jika Anda ingin berlatih sendiri, dataset saya dapat diunduh di bawah tautan.
Tautan ke dataset