Kata Pengantar
Ada banyak tutorial di Internet yang menjelaskan cara kerja LDA (Alokasi Dirichlet Laten) dan cara menerapkannya. Contoh-contoh pelatihan LDA sering ditunjukkan pada set data "contoh", seperti "20 newsgroup dataset," yang tersedia di sklearn.
Fitur pelatihan tentang contoh dataset "contoh" adalah bahwa data selalu ada dalam urutan dan mudah ditumpuk di satu tempat. Saat melatih model produksi, data yang diperoleh langsung dari sumber nyata biasanya sebaliknya:
- Banyak emisi.
- Markup salah (jika ada).
- Ketidakseimbangan kelas yang sangat kuat dan distribusi jelek dari setiap parameter dataset.
- Untuk teks, ini adalah: kesalahan tata bahasa, sejumlah besar kata langka dan unik, multibahasa.
- Cara penyimpanan data yang tidak nyaman (format berbeda atau jarang, perlunya penguraian)
Secara historis, saya mencoba belajar dari contoh-contoh yang sedekat mungkin dengan realitas-realitas produksi karena dengan cara inilah orang dapat sepenuhnya merasakan area masalah dari jenis tugas tertentu. Begitu juga dengan LDA, dan dalam artikel ini saya ingin berbagi pengalaman saya - bagaimana menjalankan LDA dari awal, pada data yang sepenuhnya mentah. Beberapa bagian dari artikel akan dikhususkan untuk memperoleh data ini, sehingga contohnya menjadi 'kasus rekayasa' lengkap.
Pemodelan topik dan LDA.
Untuk memulai, pertimbangkan apa yang LDA lakukan secara umum dan tugas apa yang digunakannya.
Paling sering, LDA digunakan untuk tugas Pemodelan Topik. Tugas semacam itu berarti tugas pengelompokan atau pengelompokan teks - sedemikian rupa sehingga setiap kelas atau kelompok berisi teks dengan topik yang sama.
Untuk menerapkan LDA ke dataset teks (selanjutnya disebut sebagai badan teks), perlu untuk mengubah tubuh menjadi matriks dokumen-istilah.
Matriks dokumen istilah adalah matriks yang memiliki ukuran dimana
N adalah jumlah dokumen dalam case, dan W adalah ukuran dari kamus case i.e. jumlah kata (unik) yang ditemukan di korpus kami. Di baris ke-i, kolom ke-j dari matriks adalah angka - berapa kali dalam teks ke-i, kata ke-j ditemukan.
LDA membangun, untuk matriks dokumen Term yang diberikan dan T dari sejumlah topik yang telah ditentukan, dua distribusi:
- Distribusi topik dalam teks. (Dalam praktiknya, diberikan oleh ukuran matriks )
- Distribusi kata berdasarkan topik (Ukuran matriks )
Nilai-nilai sel-sel matriks ini, masing-masing, adalah probabilitas bahwa topik ini terkandung dalam dokumen ini (atau proporsi topik dalam dokumen, jika kita menganggap dokumen sebagai campuran dari berbagai topik) untuk matriks 'Distribusi topik dalam teks'.
Untuk matriks 'Distribusi kata berdasarkan tema', nilainya adalah probabilitas bertemu kata j dalam teks dengan topik i, secara kualitatif, kita dapat menganggap angka-angka ini sebagai koefisien yang mengkarakterisasi bagaimana kata ini khas untuk topik ini.
Harus dikatakan bahwa kata topik bukanlah definisi "sehari-hari" dari kata ini. LDA mengalokasikan T untuk itu, tetapi topik macam apa ini dan apakah mereka sesuai dengan topik teks yang terkenal, seperti: 'Olahraga', 'Sains', 'Politik' tidak diketahui. Dalam hal ini, lebih tepat untuk membicarakan topik sebagai semacam entitas abstrak, yang didefinisikan oleh garis dalam matriks distribusi kata berdasarkan topik dan dengan beberapa probabilitas yang sesuai dengan teks ini, jika Anda dapat membayangkannya sebagai kumpulan kumpulan kata karakteristik yang bertemu bersama dengan probabilitas yang sesuai. (dari tabel) dalam satu set teks tertentu.
Jika Anda tertarik untuk mempelajari lebih detail dan 'dalam formula' bagaimana LDA dilatih dan bekerja, berikut adalah beberapa bahan (yang digunakan oleh penulis):
Kami mendapatkan data liar
Untuk 'pekerjaan laboratorium' kami, kami membutuhkan set data khusus dengan kekurangan dan fiturnya sendiri. Anda bisa mendapatkannya di tempat yang berbeda: unduh ulasan dari Kinopoisk, artikel Wikipedia, berita dari beberapa portal berita, kami akan mengambil opsi yang sedikit lebih ekstrem - posting dari komunitas VKontakte.
Kami akan melakukan ini seperti ini:
- Kami memilih beberapa pengguna VK.
- Kami mendapatkan daftar semua teman-temannya.
- Untuk setiap teman, kami mengambil semua komunitasnya.
- Untuk setiap komunitas dari setiap teman, kami memompa keluar posting komunitas pertama n (n = 100) dan menggabungkannya menjadi satu konten teks komunitas.
Alat dan artikel
Untuk mengunduh posting kita akan menggunakan modul vk untuk bekerja dengan VKontakte API, untuk Python. Salah satu momen paling rumit ketika menulis aplikasi menggunakan VKontakte API adalah otorisasi, untungnya, kode yang melakukan pekerjaan ini sudah ditulis dan berada dalam domain publik, kecuali untuk vk, saya menggunakan modul otorisasi kecil - vkauth.
Tautan ke modul dan artikel yang digunakan untuk mempelajari VKontakte API:
Menulis kode
Jadi, menggunakan vkauth, masuk:
Dalam prosesnya, modul kecil ditulis berisi semua fungsi yang diperlukan untuk mengunduh konten dalam format yang sesuai, yang tercantum di bawah ini, mari kita bahas:
def get_friends_ids(api, user_id): ''' For a given API object and user_id returns a list of all his friends ids. ''' friends = api.friends.get(user_id=user_id, v = '5.68') friends_ids = friends['items'] return friends_ids def get_user_groups(api, user_id, moder=True, only_open=True): ''' For a given API user_id returns list of all groups he subscribed to. Flag model to get only those groups where user is a moderator or an admin) Flag only_open to get only public(open) groups. ''' kwargs = {'user_id' : user_id, 'v' : '5.68' } if moder == True: kwargs['filter'] = 'moder' if only_open == True: kwargs['extended'] = 1 kwargs['fields'] = ['is_closed'] groups = api.groups.get(**kwargs) groups_refined = [] for group in groups['items']: cond_check = (only_open and group['is_closed'] == 0) or not only_open if cond_check: refined = {} refined['id'] = group['id'] * (-1) refined['name'] = group['name'] groups_refined.append(refined) return groups_refined def get_n_posts_text(api, group_id, n_posts=50): ''' For a given api and group_id returns first n_posts concatenated as one text. ''' wall_contents = api.wall.get(owner_id = group_id, count=n_posts, v = '5.68') wall_contents = wall_contents['items'] text = '' for post in wall_contents: text += post['text'] + ' ' return text
Pipa terakhir adalah sebagai berikut:
Gagal
Secara umum, proses pengunduhan data tidak sulit dengan sendirinya, Anda harus memperhatikan hanya dua hal:
- Terkadang, karena privasi beberapa komunitas, Anda akan menerima kesalahan akses, kadang-kadang kesalahan lain akan diselesaikan dengan menginstal coba, kecuali di tempat yang tepat.
- VK memiliki batasan jumlah permintaan per detik.
Saat membuat sejumlah besar permintaan, misalnya dalam satu lingkaran, kami juga akan menemukan kesalahan. Masalah ini dapat diselesaikan dengan beberapa cara:
- Bodoh dan blak-blakan: Tetaplah tidur (beberapa) setiap 3 permintaan. Ini dilakukan dalam satu baris dan sangat memperlambat pembongkaran, dalam situasi di mana volume data tidak besar, dan tidak ada waktu untuk metode yang lebih canggih - ini cukup dapat diterima. (Diterapkan dalam artikel ini)
- Memahami pekerjaan permintaan Polling Panjang https://vk.com/dev/using_longpoll
Dalam tulisan ini, metode yang sederhana dan lambat dipilih, di masa depan, saya mungkin akan menulis artikel mikro tentang cara untuk memotong atau mengurangi pembatasan jumlah permintaan per detik.
Ringkasan
Dengan "beberapa" pengguna memiliki ~ 150 teman, mereka berhasil mendapatkan 4.679 teks - masing-masing mencirikan komunitas VK tertentu. Ukuran teks sangat bervariasi dan ditulis dalam banyak bahasa - beberapa di antaranya tidak cocok untuk tujuan kita, tetapi kita akan membicarakannya sedikit lebih jauh.
Tubuh utama

Mari kita pergi melalui semua blok pipa kita - pertama, pada mandatory (Ideal), kemudian pada sisanya - mereka hanya dari kepentingan terbesar.
Countvectorizer
Sebelum mengajar LDA, kita perlu mempresentasikan dokumen kita dalam bentuk matriks dokumen Jangka. Ini biasanya termasuk operasi seperti:
- Menghapus puttuctions / angka / token yang tidak perlu.
- Tokenisasi (presentasi sebagai daftar kata)
- Menghitung kata-kata, menyusun matriks dokumen termal.
Semua tindakan ini di sklearn diimplementasikan dengan mudah dalam kerangka satu entitas program - sklearn.feature_extraction.text.CountVectorizer.
Tautan Dokumentasi
Yang perlu Anda lakukan adalah:
count_vect = CountVectorizer(input='filename', stop_words=stopwords, vocabulary=voc) dataset = count_vect.fit_transform(train_names)
Lda
Demikian pula dengan CountVectorizer, LDA diimplementasikan dengan sempurna di Sklearn dan kerangka kerja lainnya, oleh karena itu, tidak ada gunanya mencurahkan banyak ruang secara langsung untuk implementasinya, dalam artikel kami yang sepenuhnya praktis.
Tautan Dokumentasi
Yang Anda butuhkan untuk memulai LDA adalah:
Preprocessing
Jika kami hanya mengambil teks kami segera setelah mengunduhnya dan mengubahnya menjadi matriks dokumen-Term menggunakan CountVectorizer, dengan tokenizer bawaan bawaan, kami akan mendapatkan matriks ukuran 4679x769801 (pada data yang saya gunakan).
Ukuran kamus kami adalah 769801. Bahkan jika kami berasumsi bahwa sebagian besar kata-kata informatif, kami masih tidak mungkin untuk mendapatkan LDA yang baik, sesuatu seperti "Kutukan Dimensi" menunggu kami, belum lagi bahwa untuk hampir semua komputer, kami hanya akan menyumbat semua RAM. Sebenarnya, sebagian besar kata-kata ini sama sekali tidak informatif. Sebagian besar dari mereka adalah:
- Emoticon, karakter, angka.
- Kata-kata unik atau sangat langka (misalnya, kata-kata Polandia dari grup dengan meme Polandia, kata-kata dieja salah atau dalam 'Albania').
- Bagian bicara yang sangat sering (mis. Preposisi dan kata ganti).
Selain itu, banyak grup di VK yang berspesialisasi secara khusus dalam gambar - hampir tidak ada tulisan di sana - teks yang terkait dengannya merosot, dalam matriks dokumen Thermal, mereka akan memberi kita hampir nol garis.
Jadi, mari kita selesaikan semuanya!
Kami tokenize semua teks, hapus tanda baca dan angka dari mereka, lihat histogram distribusi teks dengan jumlah kata:

Kami menghapus semua teks yang lebih kecil dari 100 kata (ada 525 di antaranya)
Sekarang kamus:
Menghapus semua token (kata) yang bukan huruf, dalam kerangka tugas kita - ini cukup dapat diterima. CountVectorizer melakukan ini sendiri, bahkan jika tidak, maka saya pikir tidak perlu memberikan contoh di sini (mereka berada dalam versi lengkap dari kode untuk artikel).
Salah satu prosedur paling umum untuk mengurangi ukuran kamus adalah dengan menghapus apa yang disebut stopwords (stopwords) - kata-kata yang tidak membawa muatan semantik dan / atau tidak memiliki pewarnaan tematik (dalam kasus kami, Pemodelan Topik). Kata-kata seperti itu dalam kasus kami adalah, misalnya:
- Ucapan dan preposisi.
- Artikel -,, a.
- Kata-kata umum: 'menjadi', 'baik', 'mungkin', dll.
Modul nltk telah membentuk daftar stopword dalam bahasa Rusia dan Inggris, tetapi mereka agak lemah. Di Internet, Anda juga dapat menemukan daftar stopword untuk bahasa apa pun dan menambahkannya ke yang ada di nltk. Jadi kita akan lakukan. Ambil stopwords tambahan dari sini:
Dalam praktiknya, ketika memecahkan masalah tertentu, daftar kata kunci secara bertahap disesuaikan dan ditambah sebagai model dilatih, karena untuk setiap dataset tertentu dan masalah ada kata-kata "tidak konsisten" spesifik. Kami juga akan mengambil kata kunci khusus setelah melatih LDA generasi pertama kami.
Dengan sendirinya, prosedur untuk menghapus stopword dibangun ke dalam CountVectorizer - kita hanya perlu daftar mereka.
Apakah yang sudah kita lakukan cukup?

Sebagian besar kata-kata yang ada di kamus kami masih tidak terlalu informatif untuk mempelajari LDA pada mereka dan tidak ada dalam daftar kata kunci. Oleh karena itu, kami menerapkan metode penyaringan lain ke data kami.
dimana
t adalah kata dari kamus.
Kasing D (banyak teks)
d adalah salah satu teks tubuh.
Kami menghitung IDF dari semua kata kami, dan memotong kata-kata dengan idf terbesar (sangat jarang) dan dengan yang terkecil (kata-kata luas).
Diperoleh setelah prosedur di atas sudah cukup cocok untuk pelatihan LDA, tetapi kami akan melakukan lebih banyak stemming - kata-kata yang sama sering ditemukan dalam dataset kami, tetapi dalam kasus yang berbeda. Untuk membendung, pymystem3 digunakan .
Setelah menerapkan pemfilteran di atas, ukuran kamus menurun dari 769801 ke
13611 dan sudah dengan data seperti itu, Anda bisa mendapatkan model LDA dengan kualitas yang dapat diterima.
Menguji, menerapkan, dan menyetel LDA
Sekarang kita memiliki dataset, preprocessing dan model yang kita latih pada dataset yang diproses, akan lebih baik untuk memeriksa kecukupan model kita, serta membangun beberapa aplikasi untuk mereka.
Sebagai aplikasi, sebagai permulaan, pertimbangkan tugas menghasilkan kata kunci untuk teks yang diberikan. Anda dapat melakukan ini dengan cara yang cukup sederhana sebagai berikut:
- Kami mendapatkan dari LDA distribusi topik untuk teks ini.
- Pilih n (misalnya, n = 2) dari topik yang paling jelas.
- Untuk setiap topik, pilih m (misalnya m = 3) kata yang paling khas.
- Kami memiliki serangkaian kata-kata n * m yang mengkarakterisasi teks yang diberikan.
Kami akan menulis kelas antarmuka sederhana yang akan menerapkan metode menghasilkan kata kunci ini:
Kami menerapkan metode kami pada beberapa teks dan melihat apa yang terjadi:
Komunitas : Agen Perjalanan "Warna Dunia"
Kata kunci: ['foto', 'sosial', 'perjalanan', 'komunitas', 'perjalanan', 'euro', 'akomodasi', 'harga', 'Polandia', 'keberangkatan']
Komunitas: Gif Makanan
Kata kunci: ['mentega', 'st', 'garam', 'pc', 'adonan', 'memasak', 'bawang', 'lada', 'gula', 'gr']
Hasil di atas bukan 'cherry pick' dan terlihat cukup memadai. Bahkan, ini adalah hasil dari model yang sudah dikonfigurasi. LDA pertama yang dilatih sebagai bagian dari artikel ini menghasilkan hasil yang jauh lebih buruk, di antara kata kunci yang sering Anda lihat, misalnya:
- Komponen komposit alamat web: www, http, ru, com ...
- Kata-kata umum
- unit: cm, meter, km ...
Penyetelan (tuning) model dilakukan sebagai berikut:
- Untuk setiap topik, pilih n (n = 5) kata paling khas.
- Kami menganggap mereka idf, sesuai dengan kasus pelatihan.
- Kami mendatangkan kata kunci 5-10% yang paling luas.
"Pembersihan" seperti itu harus dilakukan dengan hati-hati, sebelum melihat 10% dari kata-kata itu. Sebaliknya, kandidat untuk dihapus harus dipilih dengan cara ini, dan kemudian kata-kata yang harus dihapus dari mereka harus dipilih secara manual.
Di suatu tempat dalam generasi 2-3 model, dengan cara yang sama memilih stopwords, untuk 5% teratas dari distribusi kata-top yang tersebar luas, kita mendapatkan:
['any', 'sepenuhnya', 'benar', 'mudah', 'berikutnya', 'internet', 'kecil', 'cara', 'sulit', 'suasana hati', 'sangat banyak', 'set', ' opsi ',' nama ',' pidato ',' program ',' kompetisi ',' musik ',' target ',' film ',' harga ',' permainan ',' sistem ',' permainan ',' permainan ',' perusahaan ' , 'bagus']
Lebih banyak aplikasi
Hal pertama yang muncul di benak saya secara khusus adalah menggunakan distribusi topik dalam teks sebagai 'embeddings' teks, dalam interpretasi ini Anda dapat menerapkan algoritma visualisasi atau pengelompokan untuk mereka, dan mencari cluster tematik 'efektif' terakhir dengan cara ini.
Mari kita lakukan ini:
term_doc_matrix = count_vect.transform(names) embeddings = lda.transform(term_doc_matrix) kmeans = KMeans(n_clusters=30) clust_labels = kmeans.fit_predict(embeddings) clust_centers = kmeans.cluster_centers_ embeddings_to_tsne = np.concatenate((embeddings,clust_centers), axis=0) tSNE = TSNE(n_components=2, perplexity=15) tsne_embeddings = tSNE.fit_transform(embeddings_to_tsne) tsne_embeddings, centroids_embeddings = np.split(tsne_embeddings, [len(clust_labels)], axis=0)
Pada output, kita mendapatkan gambar berikut:

Persilangan adalah pusat gravitasi (cenroid) dari kelompok.
Pada gambar tSNE dari embeddings, dapat dilihat bahwa cluster yang dipilih menggunakan KMeans cukup terhubung dan paling sering set terpisah secara spasial.
Yang lainnya, terserah Anda.
Tautan ke semua kode: https://gitlab.com/Mozes/VK_LDA