Rantaian Markov adalah mudah: mari kita lihat prinsipnya secara terperinci. Menambah tweet ke pangkalan data dan menyiarkan ke Twitter

Ia diterangkan bagaimana untuk melatih rangkaian neutron supaya ia memainkan Mario atau mengawal robot. Tapi boleh rangkaian neural menjana teks? Rantaian Markov boleh membantu dengan ini.

Itulah sebabnya saya "menyukai" Wikipedia bahasa Rusia, kerana sebarang fenomena/persamaan/peraturan yang mudah, terutamanya daripada matematik, diterangkan dengan serta-merta dengan cara yang umum, dengan formula yang membingungkan yang anda tidak dapat memahaminya tanpa setengah liter. Lebih-lebih lagi, pengarang artikel tidak peduli untuk memberikan penerangan yang mudah (sekurang-kurangnya beberapa ayat) bahasa manusia, dan pergi terus ke formula.

Sekiranya seseorang ingin mengetahui apa itu rantai Markov, maka dalam terjemahan pertama dia akan mengetahui bahawa:
"Rantai Markov adalah urutan peristiwa rawak dengan bilangan hasil yang terhingga atau boleh dikira, dicirikan oleh sifat yang, secara longgar, dengan masa kini yang tetap, masa depan adalah bebas daripada masa lalu. Dinamakan sebagai penghormatan kepada A. A. Markov (senior)."

Dan ini walaupun pada hakikatnya idea asas rantai Markov sangat mudah, tetapi mustahil untuk memahami ini dari Wikipedia tanpa pendidikan matematik.

Rantaian Markov hanyalah penerangan tentang kebarangkalian peralihan sistem dari satu keadaan ke keadaan yang lain. Semua keadaan boleh diterangkan oleh bucu graf. Sebagai contoh, bucu tersebut boleh menjadi kedudukan manusia: [berbaring], [duduk], [berdiri], [berjalan]

Di sini anda dapat melihat bahawa graf diarahkan, yang bermaksud bahawa tidak mungkin untuk pergi dari setiap negeri ke negeri lain. Sebagai contoh, jika anda berbaring, adalah mustahil untuk berjalan dengan segera. Anda perlu duduk dahulu, kemudian berdiri, dan kemudian berjalan. Tetapi anda boleh jatuh dan akhirnya berbaring dari mana-mana kedudukan))
Setiap sambungan mempunyai kebarangkalian tertentu. Jadi, sebagai contoh, kebarangkalian untuk jatuh dari kedudukan berdiri adalah sangat kecil; ia lebih berkemungkinan untuk berdiri lebih jauh, berjalan atau duduk. Jumlah semua kebarangkalian ialah 1.

Antara lain, rantai Markov membolehkan anda menjana acara. Satu cara atau yang lain, kebanyakan penjana teks dibina pada rantai Markov.

Mari cuba menulis penjana pai.

pai

Pai - quatrains tanpa rima, tanda baca, nombor, tanpa huruf besar. Bilangan suku kata hendaklah 9-8-9-8.


Kebanyakan penjana teks menggunakan penganalisis morfologi. Tetapi kami akan memudahkannya. Mari kita pecahkan perkataan kepada suku kata dan hitung kebarangkalian bahawa satu suku kata datang selepas suku kata yang lain. Iaitu, nod graf akan menjadi suku kata, tepi, dan pemberatnya - kekerapan suku kata kedua selepas yang pertama.
Seterusnya, kami akan memberi makan program lima puluh pai.

Sebagai contoh, selepas suku kata "di" mungkin terdapat suku kata berikut (tepi dan beratnya):
"chem" (1) "ho" (4) "saya" (1) "du" (2) "chi" (4) "yatel" (4) "pergi" (5) "ku" (1) " " (9) "su"(1) "vych"(3) "mi"(1) "kos"(1) "ob"(1) "det"(2) "memandu"(1) "uchi"(1 ) "mu"(1) "bi"(1) "tse"(1) "int"(2) "tom"(1) "ko"(1) "aci"(1) "nes"(1) " det"(1) "but"(1) "vez"(1) "meth"(1) "vet"(1) "dia"(1) "you"(1)

Sekarang semua yang anda perlu lakukan ialah mengambil suku kata rawak (contohnya, "di"). Jumlah semua suku kata yang datang selepasnya ialah 58. Sekarang anda perlu mengambil suku kata seterusnya, dengan mengambil kira kekerapan (bilangan) suku kata ini:

size_t nth = rand() % count;

saiz_t semua = 0;

untuk (const auto &n: seterusnya) (

Semua += n.count;

jika (semua >= n)

kembali n.perkataan;

Oleh itu, kita menjana baris supaya baris pertama mempunyai 9 suku kata, yang kedua - 8, kemudian 9 dan 8, kita dapat:

Suatu ketika dahulu ada jenaka tentang jam loceng
dia telah diculik semasa itu
bos awak ada di sini ya
Sofa kesan Onegin

Setakat ini ia tidak kelihatan seperti teks yang sangat koheren. Perkataan yang tidak wujud (“poku”) sering ditemui. Kini hanya terdapat satu suku kata sebagai kunci. Tetapi sukar untuk membina ayat berdasarkan satu suku kata. Mari kita tingkatkan bilangan suku kata yang berdasarkannya kita akan menjana suku kata seterusnya kepada sekurang-kurangnya 3:

Asfalt yang cukup untuk minda
sudah pukul tujuh dibahagikan dengan
meja dibawa keluar, kotak hitam adalah
dia membesar, membalas dendam, menemui
Berikut ialah pai pertama yang boleh disalah anggap sebagai ditulis oleh seseorang.
Untuk menjadikan teks lebih bermakna, anda perlu menggunakan penganalisis morfologi, dan kemudian nod tidak akan menjadi suku kata, tetapi meta-penerangan perkataan (contohnya, "kata kerja, majmuk, masa lampau").

Program sedemikian sudah membolehkan anda menulis teks yang lebih "bermakna". Sebagai contoh, rooter ialah artikel yang ditulis oleh penjana teks saintifik, yang telah disemak dan juga diterbitkan dalam jurnal saintifik.

Rantaian Markov ialah satu siri peristiwa di mana setiap peristiwa berikutnya bergantung pada yang sebelumnya. Dalam artikel ini kita akan mengkaji konsep ini dengan lebih terperinci.

Rantaian Markov ialah cara biasa dan agak mudah untuk memodelkan peristiwa rawak. Digunakan dalam kebanyakan kawasan yang berbeza, daripada penjanaan teks kepada pemodelan kewangan. Paling banyak contoh terkenal ialah SubredditSimulator. DALAM dalam kes ini Rantaian Markov digunakan untuk mengautomasikan penciptaan kandungan sepanjang subreddit.

Rantaian Markov jelas dan mudah digunakan kerana ia boleh dilaksanakan tanpa menggunakan sebarang konsep statistik atau matematik. Rantaian Markov sesuai untuk mempelajari pemodelan kebarangkalian dan sains data.

Senario

Bayangkan hanya ada dua keadaan cuaca: Ia boleh sama ada cerah atau mendung. Anda sentiasa boleh menentukan cuaca dengan tepat masa ini. Dijamin cerah atau mendung.

Sekarang anda ingin belajar cara meramal cuaca untuk esok. Secara intuitif, anda memahami bahawa cuaca tidak boleh berubah secara mendadak dalam satu hari. Ini dipengaruhi oleh banyak faktor. Cuaca esok secara langsung bergantung pada cuaca semasa, dsb. Oleh itu, untuk meramalkan cuaca, anda mengumpul data selama beberapa tahun dan membuat kesimpulan bahawa selepas hari mendung, kebarangkalian hari yang cerah ialah 0.25. Adalah logik untuk mengandaikan bahawa kebarangkalian dua hari mendung berturut-turut ialah 0.75, kerana kita hanya mempunyai dua kemungkinan keadaan cuaca.

Kini anda boleh meramalkan cuaca beberapa hari lebih awal berdasarkan cuaca semasa.

Contoh ini menunjukkan konsep kunci rantai Markov. Rantaian Markov terdiri daripada satu set peralihan yang ditentukan oleh taburan kebarangkalian, yang seterusnya memenuhi sifat Markov.

Sila ambil perhatian bahawa dalam contoh taburan kebarangkalian bergantung hanya pada peralihan dengan hari semasa kepada yang seterusnya. ini harta yang unik Proses Markov - ia melakukan ini tanpa menggunakan memori. Biasanya, pendekatan ini tidak dapat mencipta urutan di mana mana-mana arah aliran diperhatikan. Sebagai contoh, sementara rantai Markov boleh mensimulasikan gaya penulisan berdasarkan kekerapan perkataan, ia tidak boleh membuat teks dengannya makna yang mendalam, kerana ia hanya boleh berfungsi dengan teks yang besar. Inilah sebab mengapa rantai Markov tidak boleh menghasilkan kandungan yang bergantung kepada konteks.

Model

Secara rasmi, rantai Markov ialah automaton kemungkinan. Taburan kebarangkalian peralihan biasanya diwakili sebagai matriks. Jika rantai Markov mempunyai N negeri yang mungkin, maka matriks akan mempunyai bentuk N x N, di mana entri (I, J) akan menjadi kebarangkalian peralihan dari keadaan I ke keadaan J. Di samping itu, matriks sedemikian mestilah stokastik, iaitu baris atau lajur mesti ditambah sehingga satu. Dalam matriks sedemikian, setiap baris akan mempunyai taburan kebarangkalian sendiri.

Pandangan umum rantai Markov dengan keadaan dalam bentuk bulatan dan tepi dalam bentuk peralihan.

Contoh matriks peralihan dengan tiga keadaan yang mungkin.

Rantaian Markov mempunyai vektor awal keadaan, diwakili sebagai matriks N x 1 Ia menerangkan taburan kebarangkalian permulaan dalam setiap keadaan N yang mungkin. Entri I menerangkan kebarangkalian rantaian bermula dalam keadaan I.

Kedua-dua struktur ini cukup memadai untuk mewakili rantai Markov.

Kami telah membincangkan cara mendapatkan kebarangkalian peralihan dari satu keadaan ke keadaan lain, tetapi bagaimana pula dengan mendapatkan kebarangkalian itu dalam beberapa langkah? Untuk melakukan ini, kita perlu menentukan kebarangkalian peralihan dari keadaan I ke keadaan J dalam langkah M. Ia sebenarnya sangat mudah. Matriks peralihan P boleh ditentukan dengan mengira (I, J) dengan menaikkan P kepada kuasa M. Untuk nilai kecil M, ini boleh dilakukan secara manual dengan pendaraban berulang. Tetapi untuk nilai yang besar M jika anda biasa dengan algebra linear, lagi cara yang berkesan menaikkan matriks kepada kuasa akan mula-mula menyerong matriks itu.

Rantaian Markov: kesimpulan

Sekarang, mengetahui apa itu rantaian Markov, anda boleh melaksanakannya dengan mudah dalam salah satu bahasa pengaturcaraan. Rantai mudah Markov adalah asas untuk belajar lebih banyak kaedah yang kompleks pemodelan.

Saya sedang melayari forum mencari soalan yang ditanya oleh pengaturcara Python semasa temu bual dan terjumpa satu soalan yang sangat menarik. Saya akan memetiknya secara bebas: "Mereka meminta saya menulis penjana karut berdasarkan rantai Markov pesanan ke-n." "Tetapi saya belum mempunyai penjana seperti itu!" - jerit saya suara dalaman- "Cepat, buka sublime dan tulis!" - dia menyambung dengan gigih. Nah, saya terpaksa menurut.

Dan di sini saya akan memberitahu anda bagaimana saya membuatnya.

Ia segera memutuskan bahawa penjana akan menyatakan semua pemikirannya di Twitter dan laman webnya. Saya memilih Flask dan PostgreSQL sebagai teknologi utama. Mereka akan berkomunikasi antara satu sama lain melalui SQLAlchemy.

Struktur.

Jadi. Dengan cara berikut model kelihatan seperti:
kelas Srt(db.Model): id = db.Column(db.Integer, primary_key = True) set_of_words = db.Column(db.Teks()) list_of_words = db.Column(db.Text()) class UpperWords(db .Model): word = db.Column(db.String(40), index = True, primary_key = True, unique = True) def __repr__(self): return self.word class Frasa(db.Model): id = db .Column(db.Integer, primary_key = True) dicipta = db.Column(db.DateTime, default=datetime.datetime.now) frasa = db.Column(db.String(140), index = True) def __repr__(self ): return str(self.phrase)
Sebagai teks sumber Ia telah memutuskan untuk mengambil sari kata daripada siri TV popular. Kelas Srt menyimpan set tertib semua perkataan daripada sari kata yang diproses untuk satu episod dan set unik perkataan yang sama ini (tanpa ulangan). Ini akan memudahkan bot mencari frasa dalam sari kata tertentu. Mula-mula ia akan menyemak sama ada set perkataan terkandung dalam set perkataan sari kata, dan kemudian melihat jika ia terletak di sana dalam mengikut susunan yang betul.

Perkataan pertama frasa daripada teks ialah perkataan rawak bermula dengan huruf besar. UpperWords digunakan untuk menyimpan perkataan tersebut. Kata-kata itu ditulis di sana dengan cara yang sama tanpa pengulangan.

Nah, kelas Frasa diperlukan untuk menyimpan tweet yang telah dijana.
Strukturnya sangat mudah.

Penghurai sari kata bagi format .srt disertakan dalam modul berasingan add_srt.py. Tidak ada yang luar biasa di sana, tetapi jika ada yang berminat, semua sumber ada di GitHub.

Penjana.

Pertama, anda perlu memilih perkataan pertama untuk tweet anda. Seperti yang dinyatakan sebelum ini, ini akan menjadi sebarang perkataan daripada model UpperWords. Pemilihannya dilaksanakan dalam fungsi:
def add_word(word_list, n): jika bukan word_list: word = db.session.query(models.UpperWords).order_by(func.random()).first().word #postgre elif len(word_list)<= n: word = get_word(word_list, len(word_list)) else: word = get_word(word_list, n) if word: word_list.append(word) return True else: return False
Pilihan perkataan ini dilaksanakan secara langsung oleh baris:

Word = db.session.query(models.UpperWords).order_by(func.random()).first().word

Jika anda menggunakan MySQL, maka anda perlu menggunakan func.rand() dan bukannya func.random(). Ini adalah satu-satunya perbezaan dalam pelaksanaan ini; semua yang lain akan berfungsi sepenuhnya sama.

Jika perkataan pertama sudah ada, fungsi melihat kepanjangan rantai, dan bergantung pada ini, memilih dengan bilangan perkataan dalam teks yang kita perlukan untuk membandingkan senarai kami (rantaian urutan ke-1) dan dapatkan perkataan seterusnya.

Dan kita mendapat perkataan seterusnya dalam fungsi get_word:
def get_word(word_list, n): queries = models.Srt.query.all() query_list = list() untuk pertanyaan dalam query: if set(word_list)<= set(query.set_of_words.split()): query_list.append(query.list_of_words.split()) if query_list: text = list() for lst in query_list: text.extend(lst) indexies = ) if text == word_list] word = text return word else: return False
Pertama sekali, skrip berjalan melalui semua sari kata yang dimuatkan dan menyemak sama ada set perkataan kami disertakan dalam set perkataan sari kata tertentu. Kemudian teks sari kata yang ditapis digabungkan menjadi satu senarai dan padanan keseluruhan frasa dicari dan kedudukan perkataan yang mengikuti frasa ini dikembalikan. Semuanya berakhir dengan pilihan kata yang buta. Semuanya seperti dalam kehidupan.
Beginilah cara perkataan ditambahkan pada senarai. Tweet itu sendiri dipasang menjadi fungsi:
def get_twit(): word_list = list() n = N manakala len(" ".join(word_list))<140: if not add_word(word_list, n): break if len(" ".join(word_list))>140: word_list.pop() pecah manakala word_list[-1][-1] tiada dalam ".?!": word_list.pop() kembalikan " ".join(word_list)
Ia sangat mudah - tweet tidak boleh melebihi 140 aksara dan diakhiri dengan tanda baca akhir ayat. Semua. Penjana telah menjalankan tugasnya.

Paparan di tapak.

Modul views.py mengendalikan paparan di tapak.
@app.route("/") def index(): return render_template("main/index.html")
Ia hanya memaparkan templat. Semua tweet akan ditarik daripadanya menggunakan js.
@app.route("/page") def page(): page = int(request.args.get("page")) diff = int(request.args.get("perbezaan")) had = 20 frasa = models.Frases.query.order_by(-models.Frasa.id).semua() halaman = math.ceil(len(frasa)/float(had)) count = len(frasa) frasa = frasa return json.dumps(( "phrases":phrases, "pages":pages, "count":count), cls=controllers.AlchemyEncoder)
Mengembalikan tweet dari halaman tertentu. Ini perlu untuk menatal tanpa had. Semuanya agak biasa. diff – bilangan tweet yang ditambahkan selepas tapak dimuatkan semasa kemas kini. Sampel tweet untuk halaman harus dialihkan mengikut jumlah ini.

Dan kemas kini itu sendiri:
@app.route("/update") def update(): last_count = int(request.args.get("count")) phrases = models.Phrases.query.order_by(-models.Phrases.id).semua( ) count = len(frasa) jika count > last_count: phrases = phrases[:count-last_count] return json.dumps(("phrases":phrases, "count":count), cls=controllers.AlchemyEncoder) else: return json .dumps(("count":count))
Di sisi pelanggan, ia dipanggil setiap n saat dan memuatkan tweet yang baru ditambah dalam masa nyata. Beginilah cara paparan tweet kami berfungsi. (Jika sesiapa berminat, anda boleh melihat kelas AlchemyEncoder dalam controllers.py, ia digunakan untuk mensiri tweet yang diterima daripada SQLAlchemy)

Menambah tweet ke pangkalan data dan menyiarkan ke Twitter.

Saya menggunakan tweepy untuk menghantar ke Twitter. Bateri yang sangat mudah, dimulakan serta-merta.

Bagaimana rupanya:
def twit(): frasa = get_twit() twited = models.Frasa(frasa=frasa) db.session.add(twited) db.session.commit() auth = tweepy.OAuthHandler(CONSUMER_KEY, CONSUMER_SECRET) auth.set_access_token(ACCESS(ACCESS , ACCESS_TOKEN_SECRET) api = tweepy.API(auth) api.update_status(status=frasa)
Saya meletakkan panggilan ke fungsi ini dalam cron.py dalam akar projek, dan, seperti yang anda mungkin rasa, ia dilancarkan oleh cron. Setiap setengah jam tweet baharu ditambahkan pada pangkalan data dan Twitter.


Semuanya berjaya!

Akhirnya.

Pada masa ini saya telah memuat turun semua sarikata untuk siri "Friends" dan "The Big Bang Theory". Setakat ini saya telah memilih darjah rantai Markov untuk sama dengan dua (apabila asas sari kata meningkat, darjah akan meningkat). Anda boleh melihat bagaimana ia berfungsi

Dalam pembinaan web dan SEO, rantai Markov digunakan untuk menghasilkan teks pseudo-bermakna berdasarkan teks sumber. Ini digunakan untuk mengecap pintu dengan kata kunci yang diberikan, untuk menaip jisim kandungan teks dan helah "hitam" yang serupa. Nasib baik, enjin carian telah belajar mengenal pasti kandungan yang dibuat berdasarkan rantai Markov secara berkesan dan mengharamkan orang yang bijak. Saya tidak akan mengajar anda teknologi sedemikian, terdapat laman web khusus untuk itu, saya hanya berminat dengan pelaksanaan perisian algoritma.


Rantaian Markov ialah urutan percubaan di mana setiap satu daripada k peristiwa tidak serasi Ai daripada kumpulan lengkap muncul. Dalam kes ini, pij kebarangkalian bersyarat bahawa peristiwa Aj berlaku dalam percubaan ke-1, dengan syarat peristiwa Ai berlaku dalam percubaan ke-1 (s - 1), tidak bergantung pada keputusan percubaan sebelumnya.

Mereka yang ingin memerah otak boleh membaca tentang model matematik. Dalam bahasa manusia, semua formula ini bermuara kepada yang berikut. Teks sumber mentakrifkan perkataan dan mengekalkan urutan perkataan yang datang selepas itu. Kemudian, berdasarkan data ini, teks baharu dibuat di mana perkataan itu sendiri dipilih secara rawak, tetapi hubungan di antara mereka dipelihara. Mari kita ambil sajak semaian sebagai contoh:

Kerana hutan, kerana gunung
Datuk Egor akan datang:
saya di atas kuda,
isteri di atas lembu,
kanak-kanak di atas betis,
cucu pada anak kambing.

Mari kita menghuraikan teks menjadi pautan dan pautan

Kerana [hutan, gunung]
hutan [disebabkan]
gunung [menunggang]
[datuk] akan datang
datuk [Egor]
Egor [sendiri]
saya sendiri [pada]
pada [kuda, lembu, anak lembu, anak-anak]
kuda [isteri]
isteri [on]
lembu [kanak-kanak]
kanak-kanak [di]
anak lembu [cucu]
cucu [pada]

Pautan dalam senarai ini mewakili perkataan unik daripada teks, dan pautan dalam kurungan segi empat sama ialah pautan - senarai perkataan yang boleh muncul selepas perkataan itu.

Apabila menjana teks daripada senarai pautan, pada lelaran pertama, pautan rawak dipilih, sambungannya ditentukan, pautan rawak dipilih daripada senarai pautan dan diterima sebagai pautan baharu. Kemudian tindakan itu diulang sehingga saiz teks yang dikehendaki dicapai. Hasilnya, sebagai contoh, mungkin seperti ini:

Egor sendiri di atas anak lembu, cucu di atas kuda, isteri di atas lembu, anak di atas lembu
Dalam contoh ini, teks yang terhasil sedikit berbeza daripada teks asal kerana teks asal sangat pendek. Jika anda mengambil kamus awal beberapa kilobait atau bahkan megabait, output akan menjadi teks yang koheren sepenuhnya, walaupun ia tidak masuk akal.

  1. // Baca teks sumber berdasarkan teks baharu yang akan dihasilkan
  2. $str = file_get_contents("markov.txt");
  3. // Tetapkan pengekodan sistem
  4. setlocale(LC_ALL, "ru_RU.CP1251");
  5. // Alih keluar aksara daripada teks kecuali nombor, huruf dan beberapa tanda baca
  6. $str = eregi_replace ("[^-a-zа-я0-9 !\?\.\,]" , " " , $str );
  7. // Bersihkan ruang sebelum menamatkan ayat
  8. $str = eregi_replace (" (1,)([!\?\.\,])" , "\\1" , $str );
  9. // Bahagikan teks kepada perkataan
  10. $tmp = preg_split ("/[[:space:]]+/is" , $str );
  11. // Tatasusunan "pautan"
  12. $words =Array();
  13. // Isikan pautan
  14. untuk($i = 0 ; $i< count ($tmp ); $i ++) {
  15. jika ($tmp [ $i + 1 ]!="" ) (
  16. $words [ $tmp [ $i ]]= $tmp [ $i + 1 ];
  17. $words = array_map("array_unique" , ​​​​$words );
  18. // Susunan perkataan awal dalam ayat
  19. $start =Array();
  20. foreach($words as $word => $links ) (
  21. jika (eg ("^[A-Z][a-Z]+" , $word )) (
  22. $mula = $perkataan ;
  23. // Hasilkan 100 ayat berdasarkan teks sumber
  24. untuk ($i = 0; $i< 100 ; $i ++) {
  25. manakala (benar) (
  26. $w = $start [ rand (0 ,(count ($start )- 1 ))];
  27. jika (eg ("[\.!\?]$" , $w )) ( teruskan; )
  28. $ayat = $w . " " ;
  29. // Bilangan perkataan dalam ayat
  30. $cnt = 1 ;
  31. // Jana tawaran
  32. manakala(benar) (
  33. $pautan = $perkataan [ $w ];
  34. // Tukar rantai
  35. $w = $words [ $w ][ rand (0 ,(count ($words [ $w ])- 1 ))];
  36. $ayat .= $w . " " ;
  37. // Jika perkataan itu berada di hujung ayat
  38. if (eg ("[\.!\?]$" , $w )) ( break; )
  39. $cnt++;
  40. // Jika penjana berada dalam gelung, maka paksa keluar
  41. jika ($cnt > 19 ) ( break; )
  42. // Ayat dengan panjang 5-20 patah perkataan dianggap berjaya
  43. jika ($cnt > 5 && $cnt< 20 ) { break; }
  44. // Dijana tawaran
  45. echo $ayat ;

Sedikit penerangan tentang bagaimana ia berfungsi. Pertama, fail "markov.txt" dimuatkan, ia mesti dalam pengekodan win-1251. Kemudian semua aksara dikeluarkan daripadanya, kecuali huruf dan beberapa tanda baca, dan kemudian ruang yang tidak perlu dipotong. Kesudahannya teks yang jelas, yang kemudiannya dibahagikan kepada perkataan individu. Itu sahaja, kami mempunyai pautan individu dalam rantaian. Sekarang kita perlu menentukan kaitan antara perkataan, iaitu perkataan mana yang boleh terletak di belakang yang mana. Ini adalah proses yang paling intensif sumber, jadi anda perlu bersabar pada fail besar. Jika penjanaan diperlukan dengan kerap, maka mungkin masuk akal untuk menyimpan pelbagai pautan dan pautan dalam beberapa pangkalan data untuk mendapatkan akses pantas kepadanya. Langkah seterusnya- mengenal pasti perkataan yang mana ayat bermula. Saya menerima syarat bahawa huruf pertama perkataan sedemikian hendaklah menggunakan huruf besar, anda boleh melakukan lebih banyak lagi definisi yang tepat. Penjanaan teks dijalankan mengikut algoritma yang diterangkan di atas, saya hanya menambah beberapa semakan terhadap gelung kepadanya.

Anda boleh melihat contoh penjana teks yang berfungsi berdasarkan rantai Markov dan skrip di atas

Artikel ini memberi idea umum tentang cara menjana teks menggunakan pemodelan proses Markov. Khususnya, kami akan memperkenalkan rantai Markov dan, sebagai amalan, kami akan melaksanakan penjana teks kecil dalam Python.

Sebagai permulaan, mari kita tulis definisi yang diperlukan, tetapi belum begitu jelas, dari halaman Wikipedia untuk sekurang-kurangnya memahami secara kasar apa yang kita hadapi:

proses Markov t t

rantai Markov

Apakah maksud semua ini? Mari kita fikirkan.

Asas

Contoh pertama adalah sangat mudah. Menggunakan ayat dari buku kanak-kanak, kita akan menguasai konsep asas rantai Markov, dan juga mentakrifkan perkara itu dalam konteks kita badan, pautan, taburan kebarangkalian dan histogram. Walaupun cadangan diberikan pada Bahasa Inggeris, intipati teori akan mudah difahami.

Cadangan ini ialah bingkai, iaitu, asas di mana teks akan dijana pada masa hadapan. Ia terdiri daripada lapan perkataan, tetapi pada masa yang sama perkataan yang unik hanya lima sahaja pautan(kita bercakap tentang Markovian rantai). Untuk kejelasan, mari warnakan setiap pautan dalam warnanya sendiri:

Dan kami menulis bilangan kemunculan setiap pautan dalam teks:

Dalam gambar di atas anda boleh melihat bahawa perkataan "ikan" muncul dalam teks 4 kali lebih kerap daripada setiap perkataan lain ( "Satu", "dua", "merah", "biru"). Iaitu, kebarangkalian menemui perkataan dalam korpus kita "ikan" 4 kali lebih tinggi daripada kebarangkalian menemui setiap perkataan lain yang ditunjukkan dalam rajah. Bercakap dalam bahasa matematik, kita boleh menentukan hukum taburan pembolehubah rawak dan mengira dengan kebarangkalian apa salah satu perkataan akan muncul dalam teks selepas perkataan semasa. Kebarangkalian dikira seperti berikut: kita perlu membahagikan bilangan kemunculan perkataan yang kita perlukan dalam korpus dengan jumlah nombor semua perkataan di dalamnya. Untuk perkataan "ikan" kebarangkalian ini ialah 50% kerana ia muncul 4 kali dalam ayat 8 perkataan. Untuk setiap pautan yang tinggal, kebarangkalian ini ialah 12.5% ​​​​(1/8).

Secara grafik mewakili pengedaran pembolehubah rawak mungkin menggunakan histogram. Dalam kes ini, kekerapan berlakunya setiap pautan dalam ayat dapat dilihat dengan jelas:

Jadi, teks kami terdiri daripada perkataan dan pautan unik, dan kami memaparkan taburan kebarangkalian penampilan setiap pautan dalam ayat pada histogram. Jika anda fikir ia tidak patut diganggu dengan statistik, baca terus. Dan mungkin ia akan menyelamatkan nyawa anda.

Intipati definisi

Sekarang mari kita tambahkan pada elemen teks kita yang sentiasa tersirat, tetapi tidak disuarakan dalam ucapan seharian - permulaan dan akhir ayat:

Mana-mana ayat yang mengandungi "permulaan" dan "akhir" yang tidak kelihatan ini, mari tambahkannya sebagai pautan kepada pengedaran kami:

Mari kita kembali kepada definisi yang diberikan pada permulaan artikel:

proses Markov - proses rawak, yang evolusinya selepas mana-mana tetapkan nilai parameter masa t tidak bergantung kepada evolusi yang terdahulu t, dengan syarat bahawa nilai proses pada masa ini ditetapkan.

rantai Markov - kes istimewa Proses Markov, apabila ruang keadaannya adalah diskret (iaitu, tidak lebih daripada boleh dikira).

Jadi apa maksudnya? Secara kasarnya, kami memodelkan proses di mana keadaan sistem pada masa berikutnya hanya bergantung pada keadaannya pada saat semasa, dan tidak bergantung dalam apa-apa cara pada semua keadaan sebelumnya.

Bayangkan apa yang ada di hadapan anda tingkap, yang hanya memaparkan keadaan semasa sistem (dalam kes kami, ia adalah satu perkataan), dan anda perlu menentukan perkataan seterusnya hanya berdasarkan data yang dibentangkan dalam tetingkap ini. Dalam korpus kami, perkataan mengikut satu sama lain mengikut pola berikut:

Oleh itu, pasangan perkataan terbentuk (walaupun akhir ayat mempunyai pasangan sendiri - makna kosong):

Mari kumpulkan pasangan ini mengikut perkataan pertama. Kita akan melihat bahawa setiap perkataan mempunyai set pautan sendiri, yang dalam konteks ayat kita boleh ikut dia:

Mari kita membentangkan maklumat ini dengan cara lain - untuk setiap pautan kami menetapkan tatasusunan semua perkataan yang mungkin muncul dalam teks selepas pautan ini:

Mari kita lihat lebih dekat. Kami melihat bahawa setiap pautan mempunyai perkataan itu boleh datang selepasnya dalam ayat. Jika kita menunjukkan gambar rajah di atas kepada orang lain, orang itu boleh, dengan sedikit kebarangkalian, membina semula kita tawaran awal, iaitu badan.

Contoh. Mari kita mulakan dengan perkataan "Mula". Seterusnya, pilih perkataan "Satu", kerana mengikut skema kami ini adalah satu-satunya perkataan yang boleh mengikuti permulaan ayat. Di sebalik perkataan "Satu" juga hanya satu perkataan boleh mengikuti - "ikan". Kini cadangan baharu dalam versi pertengahan kelihatan seperti "Satu ikan". Selanjutnya keadaan menjadi lebih rumit - untuk "ikan" boleh ada perkataan dengan kebarangkalian yang sama sebanyak 25% "dua", "merah", "biru" dan akhir ayat "Tamat". Jika kita mengandaikan bahawa perkataan seterusnya ialah "dua", pembinaan semula akan diteruskan. Tetapi kita boleh memilih pautan "Tamat". Dalam kes ini, berdasarkan skema kami, ayat akan dijana secara rawak yang sangat berbeza daripada korpus - "Satu ikan".

Kami hanya membuat simulasi proses Markov- mengenal pasti setiap perkataan seterusnya hanya berdasarkan pengetahuan tentang perkataan semasa. Untuk memahami bahan sepenuhnya, mari bina gambar rajah yang menunjukkan kebergantungan antara unsur-unsur di dalam korpus kita. Bujur mewakili pautan. Anak panah membawa kepada pautan berpotensi yang boleh mengikut perkataan dalam bujur. Di sebelah setiap anak panah ialah kebarangkalian pautan seterusnya akan muncul selepas yang semasa:

Hebat! Kami telah belajar maklumat yang diperlukan untuk meneruskan dan menganalisis model yang lebih kompleks.

Memperluaskan asas perbendaharaan kata

Dalam bahagian artikel ini kami akan membina model mengikut prinsip yang sama seperti sebelumnya, tetapi dalam penerangan kami akan meninggalkan beberapa langkah. Jika anda menghadapi sebarang kesulitan, kembali kepada teori di blok pertama.

Mari ambil empat lagi petikan daripada pengarang yang sama (juga dalam bahasa Inggeris, ia tidak akan merugikan kita):

"Hari ini awak Adakah awak. Itu lebih benar daripada benar. Tidak ada seorang pun yang hidup yang lebih daripada kamu.”

« Kamu ada otak di kepala anda. Anda mempunyai kaki dalam kasut anda. Anda boleh memandu sendiri ke mana-mana arah yang anda pilih. Awak sendiri."

"Semakin banyak yang anda baca, semakin banyak perkara yang anda akan ketahui." Lebih banyak yang anda pelajari, lebih banyak tempat yang anda akan pergi."

"Fikir kiri dan berfikir betul dan berfikir rendah dan berfikir tinggi. Oh, mereka berfikir awak boleh fikirkan jika hanya anda mencuba."

Kerumitan korpus telah meningkat, tetapi dalam kes kami ini hanya tambahan - kini penjana teks akan dapat menghasilkan ayat yang lebih bermakna. Hakikatnya ialah dalam mana-mana bahasa terdapat perkataan yang muncul dalam pertuturan lebih kerap daripada yang lain (contohnya, kami menggunakan preposisi "dalam" lebih kerap daripada perkataan "kriogenik"). Bagaimana lebih banyak perkataan dalam korpus kami (dan oleh itu kebergantungan di antara mereka), lebih banyak maklumat penjana mempunyai tentang perkataan yang paling mungkin muncul dalam teks selepas perkataan semasa.

Cara paling mudah untuk menerangkan perkara ini adalah dari sudut pandangan program. Kami tahu bahawa untuk setiap pautan terdapat satu set perkataan yang boleh mengikutinya. Dan juga, setiap perkataan dicirikan oleh bilangan penampilannya dalam teks. Kami memerlukan beberapa cara untuk menangkap semua maklumat ini di satu tempat; Untuk tujuan ini, kamus yang menyimpan pasangan "(kunci, nilai)" adalah paling sesuai. Kunci kamus akan merekodkan keadaan semasa sistem, iaitu, salah satu pautan badan (contohnya, "yang" dalam gambar di bawah); dan kamus lain akan disimpan dalam nilai kamus. Dalam kamus bersarang, kunci ialah perkataan yang boleh muncul dalam teks selepas pautan semasa korpus ( "berfikir" Dan "lebih" boleh pergi selepas dalam teks "yang"), dan nilai ialah bilangan kemunculan perkataan ini dalam teks selepas pautan kami (perkataan "berfikir" muncul dalam teks selepas perkataan "yang" 1 kali, perkataan "lebih" selepas perkataan itu "yang"- 4 kali):

Baca semula perenggan di atas beberapa kali untuk memastikan anda memahaminya dengan tepat. Sila ambil perhatian bahawa kamus bersarang dalam kes ini adalah histogram yang sama, ia membantu kami menjejak pautan dan kekerapan penampilannya dalam teks berbanding perkataan lain. Perlu diingatkan bahawa asas perbendaharaan kata sedemikian adalah sangat kecil untuk penjanaan teks yang betul bahasa semula jadi- ia sepatutnya mengandungi lebih daripada 20,000 perkataan, atau lebih baik lagi, lebih daripada 100,000 Dan lebih baik lagi, lebih daripada 500,000 tetapi mari kita lihat asas perbendaharaan kata yang kita ada.

Rantaian Markov dalam kes ini dibina sama dengan contoh pertama - setiap perkataan seterusnya dipilih hanya berdasarkan pengetahuan tentang perkataan semasa, semua perkataan lain tidak diambil kira. Tetapi terima kasih kepada penyimpanan dalam kamus data tentang perkataan yang muncul lebih kerap daripada yang lain, kami boleh menerima apabila memilih keputusan termaklum. Mari lihat contoh khusus:

Lagi:

Iaitu, jika perkataan semasa adalah perkataan "lebih", selepas itu boleh terdapat perkataan dengan kebarangkalian yang sama sebanyak 25% "benda" Dan "tempat", dan dengan kebarangkalian 50% - perkataan "itu". Tetapi kebarangkalian semuanya boleh sama:

Fikirkan:

Bekerja dengan Windows

Sehingga kini, kami hanya menganggap tingkap sebesar satu perkataan. Anda boleh meningkatkan saiz tetingkap supaya penjana teks menghasilkan lebih banyak ayat "disahkan". Ini bermakna semakin besar tingkap, semakin kecil sisihan dari badan semasa penjanaan. Meningkatkan saiz tetingkap sepadan dengan peralihan rantai Markov kepada lebih banyak perintah tinggi. Sebelum ini, kami membina litar tertib pertama; untuk tetingkap, dua perkataan akan menghasilkan litar tertib kedua, tiga akan menghasilkan litar tertib ketiga, dan seterusnya.

Tingkap- ini adalah data dalam keadaan sekarang sistem yang digunakan untuk membuat keputusan. Jika kita sepadan tingkap besar dan set data yang kecil, maka kemungkinan besar kita akan menerima ayat yang sama setiap kali. Mari kita ambil asas perbendaharaan kata dari contoh pertama kami dan kembangkan tetingkap kepada saiz 2:

Sambungan ini bermakna setiap tetingkap kini hanya mempunyai satu pilihan untuk keadaan sistem yang seterusnya - tidak kira apa yang kami lakukan, kami akan sentiasa menerima hukuman yang sama, sama dengan kes kami. Oleh itu, untuk bereksperimen dengan tingkap, dan untuk penjana teks mengembalikan kandungan unik, simpan stok asas kosa kata daripada 500,000 patah perkataan.

Pelaksanaan dalam Python

Struktur data diktogram

Diktogram (dikt ialah jenis data kamus terbina dalam Python) akan memaparkan hubungan antara pautan dan kekerapan kejadiannya dalam teks, iaitu pengedarannya. Tetapi pada masa yang sama, ia akan mempunyai harta kamus yang kami perlukan - masa pelaksanaan program tidak akan bergantung pada jumlah data input, yang bermaksud kami mencipta algoritma yang berkesan.

Import Diktogram kelas rawak(dict): def __init__(self, iterable=None): # Mulakan pengedaran kami sebagai objek baru kelas, # tambah elemen sedia ada super(Dictogram, self).__init__() self.types = 0 # bilangan kunci unik dalam pengedaran self.tokens = 0 # jumlah bilangan semua perkataan dalam pengedaran jika boleh diulang: self.update( iterable) def kemas kini (self, iterable): # Kemas kini pengedaran dengan elemen daripada set data # iterable sedia ada untuk item dalam iterable: jika item dalam diri: self += 1 self.tokens += 1 else: self = 1 self. jenis += 1 self.token += 1 def count(self, item): # Kembalikan nilai pembilang item, atau 0 jika item dalam self: return self return 0 def return_random_word(self): random_key = random.sample(self, 1) # Cara lain: # rawak .choice(histogram.keys()) return random_key def return_weighted_random_word(self): # Jana nombor rawak pseudo antara 0 dan (n-1), # dengan n ialah jumlah bilangan perkataan random_int = random.randint(0, self.tokens-1 ) indeks = 0 list_of_keys = self.keys() # print "indeks rawak:", random_int untuk i dalam julat(0, self.types): indeks += self] # print index if(index > random_int): # print list_of_keys [i] return list_of_keys[i]

Pembina struktur Dictogram boleh menghantar sebarang objek yang boleh diulang. Unsur-unsur objek ini akan menjadi perkataan untuk memulakan Diktogram, contohnya, semua perkataan daripada buku. Dalam kes ini, kami mengira elemen supaya untuk mengakses mana-mana daripada mereka, kami tidak perlu melalui keseluruhan set data setiap kali.

Kami juga membuat dua fungsi untuk mengembalikan perkataan rawak. Satu fungsi memilih kunci rawak dalam kamus, dan satu lagi, dengan mengambil kira bilangan kemunculan setiap perkataan dalam teks, mengembalikan perkataan yang kita perlukan.

Struktur rantai Markov

daripada histogram import Dictogram def make_markov_model(data): markov_model = dict() untuk i dalam julat(0, len(data)-1): jika data[i] dalam markov_model: # Hanya tambahkan pada pengedaran yang sedia ada markov_model].update( ]) else: markov_model] = Dictogram() return markov_model

Dalam pelaksanaan di atas, kami mempunyai kamus yang menyimpan tetingkap sebagai kunci dalam pasangan "(kunci, nilai)" dan pengedaran sebagai nilai dalam pasangan itu.

Struktur rantai Markov pesanan ke-n

daripada histogram import Dictogram def make_higher_order_markov_model(order, data): markov_model = dict() untuk i dalam julat(0, len(data)-order): # Buat tetingkap tetingkap = tuple(data) # Tambah pada kamus jika tetingkap masuk markov_model: # Lampirkan pada edaran sedia ada markov_model.update() else: markov_model = Dictogram() return markov_model

Sangat serupa dengan pesanan pertama rantai Markov, tetapi dalam kes ini kami menyimpan permotoran sebagai kunci dalam pasangan "(kunci, nilai)" dalam kamus. Kami menggunakannya dan bukannya senarai, kerana tuple dilindungi daripada sebarang perubahan, dan ini penting bagi kami - lagipun, kunci dalam kamus tidak sepatutnya berubah.

Penghuraian model

Bagus, kami telah melaksanakan kamus. Tetapi bagaimana kita boleh menjana kandungan berdasarkan keadaan semasa dan langkah ke keadaan seterusnya? Mari kita lihat model kami:

Dari histogram import Dictogram import rawak daripada collections import deque import re def generate_random_start(model): # Untuk menjana sebarang perkataan permulaan, nyahkomen baris: # return random.choice(model.keys()) # Untuk menjana perkataan permulaan "betul" , gunakan kod di bawah: # Betul perkataan awal- ini adalah permulaan ayat dalam korpus jika "END" dalam model: seed_word = "END" manakala seed_word == "END": seed_word = model["END"].return_weighted_random_word() return seed_word return rawak. pilihan(model .keys()) def generate_random_sentence(length, markov_model): current_word = generate_random_start(markov_model) ayat = untuk i dalam julat(0, length): current_dictogram = markov_model random_weighted_word = current_dictogram.return_weighted_random_word() current_word = ayat_rawak_berat. (perkataan_semasa) ayat = sentence.capitalize() return " ".join(ayat) + "." ayat balik

Apa yang akan datang?

Cuba fikirkan di mana anda boleh menggunakan penjana teks berdasarkan rantai Markov sendiri. Cuma jangan lupa bahawa perkara yang paling penting ialah cara anda menghuraikan model dan apakah sekatan istimewa yang anda tetapkan pada penjanaan. Pengarang artikel ini, sebagai contoh, semasa mencipta penjana tweet, menggunakan tetingkap besar, mengehadkan kandungan yang dihasilkan kepada 140 aksara, dan hanya menggunakan perkataan "betul" untuk memulakan ayat, iaitu, yang merupakan permulaan ayat dalam korpus.