Mlolongo wa Markov ni rahisi: hebu tuangalie kanuni kwa undani. Kuongeza tweets kwenye hifadhidata na kutuma kwenye Twitter

Ilielezwa jinsi ya kutoa mafunzo kwa mtandao wa nyutroni ili kucheza Mario au kudhibiti roboti. Lakini unaweza mtandao wa neva kuzalisha maandishi? Minyororo ya Markov inaweza kusaidia na hii.

Ndio maana "ninapenda" Wikipedia ya lugha ya Kirusi, kwa sababu jambo lolote rahisi / equation / sheria, haswa kutoka kwa hisabati, inaelezewa mara moja kwa njia ya jumla, na fomula za kushangaza ambazo huwezi kuzijua bila. nusu lita. Kwa kuongezea, waandishi wa vifungu hawajisumbui kutoa maelezo rahisi (angalau sentensi kadhaa) lugha ya binadamu, na nenda moja kwa moja kwenye fomula.

Ikiwa mtu anataka kujua minyororo ya Markov ni nini, basi katika tafsiri ya kwanza atagundua kuwa:
"Mlolongo wa Markov ni mlolongo matukio ya nasibu yenye idadi ya mwisho au inayoweza kuhesabika ya matokeo, yenye sifa ya mali ambayo, tukizungumza kwa ulegevu, ikiwa na sasa isiyobadilika, siku zijazo hazitegemei yaliyopita. Imetajwa kwa heshima ya A. A. Markov (mwandamizi)."

Na hii licha ya ukweli kwamba wazo la msingi la minyororo ya Markov ni rahisi sana, lakini haiwezekani kuelewa hii kutoka kwa Wikipedia bila elimu ya hisabati.

Minyororo ya Markov ni maelezo tu ya uwezekano wa mabadiliko ya mfumo kutoka hali moja hadi nyingine. Majimbo yote yanaweza kuelezewa na wima ya grafu. Kwa mfano, vipeo hivyo vinaweza kuwa nafasi za kibinadamu: [kulala], [kuketi], [kusimama], [kutembea]

Hapa unaweza kuona kwamba grafu inaelekezwa, ambayo ina maana kwamba haiwezekani kupata kutoka kila hali hadi nyingine. Kwa mfano, ikiwa umelala chini, haiwezekani kutembea mara moja. Unapaswa kukaa chini kwanza, kisha kusimama, na kisha tu kutembea. Lakini unaweza kuanguka na kuishia kulala chini kutoka kwa nafasi yoyote))
Kila muunganisho una uwezekano fulani. Kwa hivyo, kwa mfano, uwezekano wa kuanguka kutoka kwa msimamo ni mdogo sana, kuna uwezekano mkubwa wa kusimama zaidi, kutembea au kukaa chini. Jumla ya uwezekano wote ni 1.

Miongoni mwa mambo mengine, minyororo ya Markov inakuwezesha kuzalisha matukio. Njia moja au nyingine, jenereta nyingi za maandishi hujengwa kwenye minyororo ya Markov.

Hebu jaribu kuandika jenereta ya pies.

Pies

Pies - quatrains bila rhyme, punctuation, namba, bila herufi kubwa. Idadi ya silabi inapaswa kuwa 9-8-9-8.


Jenereta nyingi za maandishi hutumia uchanganuzi wa kimofolojia. Lakini tutafanya iwe rahisi. Hebu tugawanye maneno katika silabi na tuhesabu uwezekano kwamba silabi moja huja baada ya silabi nyingine. Hiyo ni, nodi za grafu zitakuwa silabi, kingo, na uzani wao - marudio ya silabi ya pili inayofuata ya kwanza.
Ifuatayo, tutalisha programu pies hamsini.

Kwa mfano, baada ya silabi "saa" kunaweza kuwa na silabi zifuatazo (kingo na uzani wao):
"chem" (1) "ho" (4) "mimi" (1) "du" (2) "chi" (4) "yatel" (4) "nilienda" (5) "ku" (1) " " (9) "su"(1) "vych"(3) "mi"(1) "kos"(1) "ob"(1) "det"(2) "aliendesha"(1) "uchi"(1) ) "mu"(1) "bi"(1) "tse"(1) "int"(2) "tom"(1) "ko"(1) "shimoni"(1) "nes"(1) " det"(1) "lakini"(1) "vez"(1) "meth"(1) "vet"(1) "dia"(1) "wewe"(1)

Sasa unachohitaji kufanya ni kuchukua silabi nasibu (kwa mfano, "saa"). Jumla ya silabi zote zinazokuja baada yake ni 58. Sasa unahitaji kuchukua silabi inayofuata, ukizingatia mara kwa mara (idadi) ya silabi hizi:

size_t nth = rand() % hesabu;

size_t zote = 0;

kwa (const auto &n: next) (

Zote += n.hesabu;

ikiwa (yote >= nth)

kurudi n.neno;

Kwa hivyo, tunatoa mistari ili mstari wa kwanza uwe na silabi 9, ya pili - 8, kisha 9 na 8, tunapata:

Wakati mmoja kulikuwa na utani kuhusu saa ya kengele
alitekwa nyara ilipokuwa
mkuu wako yuko hapa ndio
Onegin madhara sofa

Kufikia sasa haionekani kuwa maandishi madhubuti. Maneno yasiyopo ("poku") mara nyingi hukutana. Sasa kuna silabi moja tu kama ufunguo. Lakini ni ngumu kuunda sentensi kulingana na silabi moja. Wacha tuongeze idadi ya silabi kwa msingi ambao tutatoa silabi inayofuata hadi angalau 3:

Lami ya kutosha kwa akili
ni saa saba imegawanywa na
meza inachukuliwa nje, sanduku nyeusi ni
alikua, alilipiza kisasi, akapatikana
Hapa ni pai ya kwanza ambayo inaweza zaidi au chini kuwa na makosa kwa kuandikwa na mtu.
Ili kufanya maandishi kuwa na maana zaidi, unahitaji kutumia vichanganuzi vya kimofolojia, na kisha nodi hazitakuwa silabi, lakini maelezo ya meta ya maneno (kwa mfano, "kitenzi, wingi, wakati uliopita").

Mipango hiyo tayari inakuwezesha kuandika maandishi zaidi "ya maana". Kwa mfano, rooter ni makala iliyoandikwa na jenereta ya maandishi ya kisayansi, ambayo ilipitiwa na hata kuchapishwa katika jarida la kisayansi.

Mlolongo wa Markov ni mfululizo wa matukio ambayo kila tukio linalofuata linategemea lile lililotangulia. Katika makala hii tutachunguza dhana hii kwa undani zaidi.

Mlolongo wa Markov ni njia ya kawaida na rahisi ya kuiga matukio nasibu. Inatumika kwa wengi maeneo mbalimbali, kutoka kwa kizazi cha maandishi hadi mfano wa kifedha. wengi zaidi mfano maarufu ni SubredditSimulator. KATIKA kwa kesi hii Mlolongo wa Markov hutumiwa kuhariri uundaji wa yaliyomo katika subreddit yote.

Mlolongo wa Markov uko wazi na ni rahisi kutumia kwa sababu unaweza kutekelezwa bila kutumia dhana zozote za takwimu au hisabati. Mlolongo wa Markov ni bora kwa kujifunza uundaji wa uwezekano na sayansi ya data.

Mazingira

Fikiria kuna mbili tu hali ya hewa: Inaweza kuwa ya jua au mawingu. Unaweza kuamua kwa usahihi hali ya hewa ndani wakati huu. Imehakikishwa kuwa wazi au mawingu.

Sasa unataka kujifunza jinsi ya kutabiri hali ya hewa ya kesho. Intuitively, unaelewa kuwa hali ya hewa haiwezi kubadilika sana kwa siku moja. Hii inathiriwa na mambo mengi. Hali ya hewa ya kesho moja kwa moja inategemea moja ya sasa, nk Hivyo, ili kutabiri hali ya hewa, unakusanya data kwa miaka kadhaa na kufikia hitimisho kwamba baada ya siku ya mawingu, uwezekano wa siku ya jua ni 0.25. Ni busara kudhani kwamba uwezekano wa siku mbili za mawingu mfululizo ni 0.75, kwa kuwa tuna hali mbili tu za hali ya hewa.

Sasa unaweza kutabiri hali ya hewa siku kadhaa mapema kulingana na hali ya hewa ya sasa.

Mfano huu unaonyesha dhana muhimu Markov minyororo. Msururu wa Markov unajumuisha seti ya mageuzi ambayo hubainishwa na usambazaji wa uwezekano, ambao unakidhi sifa ya Markov.

Tafadhali kumbuka kuwa katika mfano usambazaji wa uwezekano unategemea tu mabadiliko na siku ya sasa kwa ijayo. Hii mali ya kipekee Mchakato wa Markov - hufanya hivi bila kutumia kumbukumbu. Kwa kawaida, mbinu hii haiwezi kuunda mlolongo ambao mwenendo wowote unazingatiwa. Kwa mfano, wakati mnyororo wa Markov unaweza kuiga mtindo wa uandishi kulingana na marudio ya matumizi ya neno, hauwezi kuunda maandishi na maana ya kina, kwani inaweza kufanya kazi na maandishi makubwa tu. Hii ndiyo sababu mlolongo wa Markov hauwezi kutoa maudhui yanayotegemea muktadha.

Mfano

Hapo awali, mnyororo wa Markov ni otomatiki inayowezekana. Usambazaji wa uwezekano wa mpito kawaida huwakilishwa kama matriki. Ikiwa mnyororo wa Markov una N mataifa yanayowezekana, basi tumbo litakuwa na fomu N x N, ambayo kuingia (I, J) itakuwa uwezekano wa mpito kutoka hali ya I hadi J. Kwa kuongeza, matrix hiyo lazima iwe stochastic, yaani, safu au safu. safu wima lazima zijumuishwe hadi moja. Katika matrix kama hiyo, kila safu itakuwa na usambazaji wake wa uwezekano.

Mtazamo wa jumla wa mnyororo wa Markov na majimbo katika mfumo wa miduara na kingo katika mfumo wa mabadiliko.

Mfano wa matrix ya mpito yenye hali tatu zinazowezekana.

Mlolongo wa Markov una vekta ya awali majimbo, yanayowakilishwa kama matrix ya N x 1. Inafafanua usambaaji wa uwezekano wa mwanzo katika kila mojawapo ya hali N inayowezekana. Ingizo ninaloelezea uwezekano wa mnyororo kuanzia katika jimbo la I.

Miundo hii miwili inatosha kabisa kuwakilisha mnyororo wa Markov.

Tayari tumejadili jinsi ya kupata uwezekano wa mabadiliko kutoka jimbo moja hadi jingine, lakini vipi kuhusu kupata uwezekano huo kwa hatua chache? Ili kufanya hivyo, tunahitaji kuamua uwezekano wa mabadiliko kutoka jimbo la I hadi J katika hatua za M. Kwa kweli ni rahisi sana. Matrix ya mpito P inaweza kuamuliwa kwa kutumia kompyuta (I, J) kwa kuinua P kwa nguvu M. Kwa maadili madogo ya M, hii inaweza kufanywa kwa mikono kwa kuzidisha mara kwa mara. Lakini kwa maadili makubwa M ikiwa unamfahamu algebra ya mstari, zaidi njia ya ufanisi kuinua matrix kwa nguvu kwanza kutapunguza matrix hiyo.

Mnyororo wa Markov: hitimisho

Sasa, ukijua mlolongo wa Markov ni nini, unaweza kuitekeleza kwa urahisi katika mojawapo ya lugha za programu. Minyororo rahisi Markov ndio msingi wa kusoma zaidi mbinu tata uundaji wa mfano.

Nilikuwa nikivinjari mabaraza nikitafuta maswali ambayo waandaaji wa programu ya Python huulizwa wakati wa mahojiano na nikapata moja nzuri sana. Nitamnukuu kwa uhuru: "Waliniuliza niandike jenereta isiyo na maana kulingana na agizo la nth Markov." "Lakini bado sina jenereta kama hiyo!" - alipiga kelele yangu sauti ya ndani- "Haraka, fungua utukufu na uandike!" - aliendelea kwa bidii. Naam, ilinibidi kutii.

Na hapa nitakuambia jinsi nilivyofanya.

Mara moja iliamuliwa kuwa jenereta ingeelezea mawazo yake yote kwenye Twitter na tovuti yake. Nilichagua Flask na PostgreSQL kama teknolojia kuu. Watawasiliana kupitia SQLAlchemy.

Muundo.

Hivyo. Kwa njia ifuatayo mifano inaonekana kama:
darasa Srt(db.Model): id = db.Column(db.Integer, primary_key = Kweli) set_of_words = db.Safuwima(db.Text()) list_of_words = db.Safuwima(db.Text()) darasa la UpperWords(db) .Mfano): neno = db.Safuwima(db.String(40), faharasa = Kweli, ufunguo_msingi = Kweli, wa kipekee = Kweli) def __repr__(binafsi): rudisha vifungu vya maneno(db.Model): id = db .Safuwima(db.Nambari, ufunguo_msingi = Kweli) imeundwa = db.Safuwima(db.DateTime, default=datetime.datetime.now) maneno = db.Safuwima(db.String(140), index = True) def __repr__(self ): rudisha str(sehemu binafsi)
Kama maandishi ya chanzo Iliamuliwa kuchukua manukuu kutoka kwa mfululizo maarufu wa TV. Darasa la Srt huhifadhi seti iliyoagizwa ya maneno yote kutoka kwa manukuu yaliyochakatwa kwa kipindi kimoja na seti ya kipekee ya maneno haya sawa (bila marudio). Hii itarahisisha roboti kutafuta fungu la maneno katika manukuu maalum. Itaangalia kwanza ikiwa seti ya maneno iko katika seti ya maneno ya manukuu, na kisha kuona ikiwa yanalala huko. kwa mpangilio sahihi.

Neno la kwanza la kifungu kutoka kwa maandishi ni neno la nasibu linaloanza na herufi kubwa. UpperWords hutumiwa kuhifadhi maneno kama haya. Maneno yameandikwa hapo kwa njia sawa bila kurudiwa.

Vizuri, darasa la Vifungu vinahitajika ili kuhifadhi tweets zilizotengenezwa tayari.
Muundo ni rahisi sana.

Kichanganuzi kidogo cha umbizo la .srt kimejumuishwa katika moduli tofauti add_srt.py. Hakuna kitu cha kushangaza hapo, lakini ikiwa kuna mtu yeyote anayevutiwa, vyanzo vyote viko kwenye GitHub.

Jenereta.

Kwanza, unahitaji kuchagua neno la kwanza kwa tweet yako. Kama ilivyoelezwa hapo awali, hii itakuwa neno lolote kutoka kwa mfano wa UpperWords. Uteuzi wake unatekelezwa katika kazi:
def add_word(word_list, n): if not word_list: word = db.session.query(models.UpperWords).order_by(func.random()).kwanza().neno #postgre elif len(orodha_ya_maneno)<= 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
Chaguo la neno hili linatekelezwa moja kwa moja na mstari:

Neno = db.session.query(models.UpperWords).order_by(func.random()).kwanza().neno

Ikiwa unatumia MySQL, basi unahitaji kutumia func.rand() badala ya func.random(). Hii ndio tofauti pekee katika utekelezaji huu; kila kitu kingine kitafanya kazi sawa kabisa.

Ikiwa neno la kwanza tayari lipo, kazi inaangalia urefu wa mnyororo, na kulingana na hii, huchagua na idadi gani ya maneno katika maandishi tunayohitaji kulinganisha orodha yetu (mlolongo wa mpangilio wa nth) na kupata. neno linalofuata.

Na tunapata neno linalofuata katika kazi ya get_word:
def get_word(word_list, n): queries = models.Srt.query.all() query_list = list() kwa hoja katika hoja: ikiwa imewekwa(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
Kwanza kabisa, hati hupitia manukuu yote yaliyopakiwa na hukagua ikiwa seti yetu ya maneno imejumuishwa katika seti ya maneno ya manukuu maalum. Kisha maandishi ya manukuu yaliyochujwa yanajumuishwa katika orodha moja na ulinganifu wa vishazi vyote hutafutwa na nafasi za maneno yanayofuata vishazi hivi hurejeshwa. Yote inaisha na uchaguzi wa kipofu wa maneno. Kila kitu ni kama katika maisha.
Hivi ndivyo maneno yanaongezwa kwenye orodha. Tweet yenyewe imekusanywa kuwa kazi:
def get_twit(): word_list = list() n = N huku len(" ".jiunge(word_list))<140: if not add_word(word_list, n): break if len(" ".join(word_list))>140: neno_list.pop() mapumziko huku neno_list[-1][-1] halipo kwenye ".?!": word_list.pop() rudisha " ".jiunge (word_list)
Ni rahisi sana - tweet lazima isizidi herufi 140 na imalizie kwa alama ya uakifishaji inayomaliza sentensi. Wote. Jenereta imefanya kazi yake.

Onyesha kwenye tovuti.

Moduli ya views.py inashughulikia onyesho kwenye tovuti.
@app.route("/") def index(): return render_template("main/index.html")
Inaonyesha kiolezo tu. Twiti zote zitatolewa kwa kutumia js.
@app.route("/ukurasa") ukurasa wa kufafanua(): ukurasa = int(request.args.get("ukurasa")) diff = int(request.args.get("tofauti")) limit = vifungu 20 = models.Phrases.query.order_by(-models.Phrases.id).all() kurasa = math.ceil(len(misemo)/float(kikomo)) count = len(misemo) vishazi = vishazi vinarudisha json.dumps(( "maneno":maneno, "kurasa":kurasa, "hesabu":hesabu), cls=controllers.AlchemyEncoder)
Hurejesha tweets kutoka kwa ukurasa maalum. Hii ni muhimu kwa usogezaji usio na mwisho. Kila kitu ni cha kawaida sana. diff - idadi ya tweets zilizoongezwa baada ya tovuti kupakiwa wakati wa sasisho. Sampuli ya tweets kwa ukurasa inapaswa kubadilishwa kwa kiasi hiki.

Na sasisho yenyewe:
@app.route("/sasisha") def update(): last_count = int(request.args.get("count")) vifungu = models.Phrases.query.order_by(-models.Phrases.id).all( ) count = len(maneno) kama hesabu > last_count: misemo = misemo[:count-last_count] rudisha json.dumps(("misemo":maneno, "hesabu":hesabu), cls=controllers.AlchemyEncoder) vinginevyo: rudisha json .dampo(("hesabu":hesabu))
Kwa upande wa mteja, inaitwa kila sekunde n na hupakia tweets mpya zilizoongezwa kwa wakati halisi. Hivi ndivyo onyesho letu la tweet linavyofanya kazi. (Ikiwa kuna mtu yeyote anavutiwa, unaweza kuangalia darasa la AlchemyEncoder katika controllers.py, inatumika kusawazisha tweets zilizopokelewa kutoka SQLAlchemy)

Kuongeza tweets kwenye hifadhidata na kutuma kwenye Twitter.

Nilitumia tweepy kuchapisha kwenye Twitter. Betri inayofaa sana, huanza mara moja.

Inaonekanaje:
def twit(): maneno = get_twit() twited = models.Vifungu(phrase=phrase) db.session.add(twited) db.session.commit() auth = tweepy.OAuthHandler(CONSUMER_KEY, CONSUMER_SECRET) auth.set_ACCESS_token , ACCESS_TOKEN_SECRET) api = tweepy.API(auth) api.update_status(status=phrase)
Nilitoa simu kwa kazi hii kwenye cron.py kwenye mzizi wa mradi, na, kama unavyoweza kudhani, imezinduliwa na cron. Kila nusu saa tweet mpya huongezwa kwenye hifadhidata na Twitter.


Kila kitu kilifanya kazi!

Hatimaye.

Kwa sasa nimepakua manukuu yote ya mfululizo wa "Marafiki" na "Nadharia ya Big Bang". Kufikia sasa nimechagua kiwango cha mnyororo wa Markov kuwa sawa na mbili (kadiri msingi wa manukuu unavyoongezeka, digrii itaongezeka). Unaweza kuona jinsi inavyofanya kazi ndani

Katika ujenzi wa wavuti na SEO, minyororo ya Markov hutumiwa kutengeneza maandishi yenye maana ya uwongo kulingana na maandishi ya chanzo. Hii inatumika kwa kugonga mlango kwa maneno muhimu yaliyotolewa, kwa kuandika wingi wa maandishi ya maudhui na mbinu kama hizo "nyeusi". Kwa bahati nzuri, injini za utafutaji zimejifunza kutambua kwa ufanisi maudhui yaliyoundwa kulingana na minyororo ya Markov na kupiga marufuku watu kama hao wajanja. Sitakufundisha teknolojia kama hizo, kuna tovuti maalum za shitty kwa hiyo, ninavutiwa tu na utekelezaji wa programu ya algorithm.


Mlolongo wa Markov ni mlolongo wa majaribio katika kila moja ambayo moja tu ya matukio ya k yasiyoendana Ai kutoka kwa kundi kamili inaonekana. Katika hali hii, uwezekano wa masharti pij(s) kwamba tukio Aj hutokea katika jaribio la sth, mradi tu tukio Ai litatokea katika jaribio la (s - 1), halitegemei matokeo ya majaribio ya awali.

Wale ambao wanataka kupiga akili zao wanaweza kusoma kuhusu mfano wa hisabati. Katika lugha ya binadamu, fomula hizi zote hujikita katika zifuatazo. Katika matini chanzi, maneno hufafanuliwa na mlolongo wa ambayo maneno huja baada yake hutunzwa. Kisha, kwa kuzingatia data hii, maandishi mapya yanaundwa ambayo maneno yenyewe huchaguliwa kwa nasibu, lakini uhusiano kati yao huhifadhiwa. Wacha tuchukue wimbo wa kitalu kama mfano:

Kwa sababu ya msitu, kwa sababu ya milima
Babu Egor anakuja:
mimi mwenyewe juu ya farasi,
mke juu ya ng'ombe,
watoto juu ya ndama,
wajukuu juu ya mbuzi wachanga.

Hebu tuchanganue maandishi katika viungo na viungo

Kwa sababu ya [misitu, milima]
misitu [kutokana na]
milima [rides]
[babu] anakuja
babu [Egor]
Egor [mwenyewe]
mimi [ni]
juu ya [farasi, ng'ombe, ndama, watoto]
farasi [mke]
mke [on]
ng'ombe [watoto]
watoto [on]
ndama [wajukuu]
wajukuu [on]

Viungo katika orodha hii vinawakilisha maneno ya kipekee kutoka kwa maandishi, na viungo katika mabano ya mraba ni viungo - orodha ya maneno ambayo yanaweza kuonekana baada ya neno hilo.

Wakati wa kutengeneza maandishi kutoka kwa orodha ya viungo, mara ya kwanza, kiungo cha nasibu kinachaguliwa, miunganisho yake imedhamiriwa, kiungo cha nasibu kinachaguliwa kutoka kwenye orodha ya viungo na kukubaliwa kama kiungo kipya. Kisha hatua inarudiwa hadi ukubwa wa maandishi unaohitajika ufikiwe. Matokeo, kwa mfano, inaweza kuwa kitu kama hiki:

Egor mwenyewe juu ya ndama, wajukuu juu ya farasi, mke juu ya ng'ombe, watoto juu ya ng'ombe
Katika mfano huu, maandishi yanayotokana yanatofautiana kidogo na maandishi asilia kwa sababu maandishi asilia ni mafupi sana. Ukichukua kamusi ya awali ya kilobaiti kadhaa au hata megabaiti, matokeo yatakuwa maandishi madhubuti, ingawa haitakuwa na maana yoyote.

  1. // Soma maandishi ya chanzo kwa msingi ambayo mpya itatolewa
  2. $str = file_get_contents("markov.txt");
  3. // Weka usimbaji wa mfumo
  4. setlocale(LC_ALL, "ru_RU.CP1251");
  5. // Ondoa herufi kutoka kwa maandishi isipokuwa nambari, herufi na alama za uakifishaji
  6. $str = eregi_replace ("[^-a-za-я0-9 !\?\.\,]" , " " , $str );
  7. // Safisha nafasi kabla ya kumalizia sentensi
  8. $str = eregi_replace (" (1,)([!\?\.\,])" , "\\1" , $str );
  9. // Gawanya maandishi kwa maneno
  10. $tmp = preg_split ("/[[:space:]]+/is" , $str );
  11. // Safu ya "viungo"
  12. $maneno =Safu();
  13. // Jaza viungo
  14. kwa($i = 0; $i< count ($tmp ); $i ++) {
  15. ikiwa ($tmp [ $i + 1 ]!= "" ) (
  16. $words [ $tmp [ $i ]]= $tmp [ $i + 1];
  17. $words = array_map("array_unique" , $words );
  18. // Msururu wa maneno ya awali katika sentensi
  19. $anza =Array();
  20. foreach($words as $word => $links ) (
  21. ikiwa (eg ("^[A-Z][a-Z]+" , $word )) (
  22. $anza = neno $;
  23. // Tengeneza sentensi 100 kulingana na matini chanzi
  24. kwa ($i = 0; $i< 100 ; $i ++) {
  25. wakati (kweli) (
  26. $w = $start [ rand (0 ,(count ($start)- 1 ))];
  27. ikiwa (eg ("[\.!\?]$" , $w )) (endelea;)
  28. $sentensi = $w . "";
  29. // Idadi ya maneno katika sentensi
  30. $cnt = 1;
  31. // Tengeneza ofa
  32. wakati (kweli) (
  33. $links = $maneno [ $w];
  34. // Badilisha mnyororo
  35. $w = $words [ $w ][ rand (0 ,(count ($words [ $w ])- 1 ))];
  36. $sentensi .= $w . "";
  37. // Ikiwa neno lilikuwa mwishoni mwa sentensi
  38. ikiwa (eg ("[\.!\?]$" , $w )) ( break; )
  39. $cnt++;
  40. // Ikiwa jenereta iko kwenye kitanzi, basi uondoe kwa nguvu
  41. ikiwa ($cnt > 19) ( mapumziko; )
  42. // Sentensi yenye urefu wa maneno 5-20 inachukuliwa kuwa yenye mafanikio
  43. ikiwa ($cnt > 5 && $cnt< 20 ) { break; }
  44. // Toleo lililotolewa
  45. echo $sentensi;

Maelezo kidogo ya jinsi yote yanavyofanya kazi. Kwanza, faili "markv.txt" imepakiwa, lazima iwe katika encoding ya win-1251. Kisha wahusika wote huondolewa kutoka humo, isipokuwa barua na alama za punctuation, na kisha nafasi zisizohitajika hukatwa. Inageuka maandishi wazi, ambayo imegawanywa katika maneno ya mtu binafsi. Hiyo ni, tuna viungo vya mtu binafsi kwenye mnyororo. Sasa tunahitaji kuamua miunganisho kati ya maneno, ambayo ni, maneno gani yanaweza kupatikana nyuma ya yale. Huu ndio mchakato unaotumia rasilimali nyingi zaidi, kwa hivyo utalazimika kuwa na subira kwenye faili kubwa. Ikiwa uzalishaji unahitajika mara kwa mara, basi labda inaeleweka kuhifadhi safu ya viungo na viungo katika hifadhidata fulani ili kuipata haraka. Hatua ifuatayo- kutambua maneno ambayo sentensi huanza. Nilikubali sharti kwamba herufi ya kwanza ya maneno kama hayo iwe na herufi kubwa, unaweza kufanya zaidi ufafanuzi sahihi. Uzalishaji wa maandishi unafanywa kulingana na algorithm iliyoelezewa hapo juu, nimeongeza tu ukaguzi kadhaa dhidi ya kuizunguka.

Unaweza kuona mfano wa kazi wa jenereta ya maandishi kulingana na minyororo ya Markov na hati iliyo hapo juu

Makala hii inatoa wazo la jumla kuhusu jinsi ya kutengeneza maandishi kwa kutumia muundo wa mchakato wa Markov. Hasa, tutaanzisha minyororo ya Markov na, kama mazoezi, tutatumia jenereta ndogo ya maandishi huko Python.

Kuanza, hebu tuandike muhimu, lakini bado sio wazi kabisa, ufafanuzi kutoka kwa ukurasa wa Wikipedia ili angalau kuelewa kile tunachoshughulika nacho:

Mchakato wa Markov t t

Mnyororo wa Markov

Je, yote haya yanamaanisha nini? Hebu tufikirie.

Misingi

Mfano wa kwanza ni rahisi sana. Kwa kutumia sentensi kutoka kwa kitabu cha watoto, tutaweza dhana ya msingi Markov minyororo, na pia kufafanua nini wao ni katika mazingira yetu mwili, viungo, usambazaji wa uwezekano na histograms. Ingawa pendekezo limetolewa Lugha ya Kiingereza, kiini cha nadharia kitakuwa rahisi kufahamu.

Pendekezo hili ni fremu, yaani, msingi juu ya msingi ambao maandishi yatatolewa katika siku zijazo. Inajumuisha maneno nane, lakini wakati huo huo maneno ya kipekee ni tano tu viungo(Tunazungumza juu ya Markovian minyororo) Kwa uwazi, wacha tupake rangi kila kiunga kwa rangi yake mwenyewe:

Na tunaandika idadi ya kuonekana kwa kila kiungo kwenye maandishi:

Katika picha hapo juu unaweza kuona kwamba neno "samaki" inaonekana katika maandishi mara 4 zaidi kuliko kila moja ya maneno mengine ( "Moja", "mbili", "nyekundu", "bluu") Hiyo ni, uwezekano wa kukutana na neno katika corpus yetu "samaki" Mara 4 zaidi ya uwezekano wa kukutana na kila neno lingine lililoonyeshwa kwenye takwimu. Kuzungumza katika lugha ya hisabati, tunaweza kuamua sheria ya usambazaji wa tofauti ya nasibu na kuhesabu kwa uwezekano gani moja ya maneno yataonekana kwenye maandishi baada ya moja ya sasa. Uwezekano umehesabiwa kama ifuatavyo: tunahitaji kugawanya idadi ya matukio ya neno tunalohitaji katika corpus na jumla ya nambari maneno yote ndani yake. Kwa neno "samaki" uwezekano huu ni 50% kwani inaonekana mara 4 katika sentensi ya maneno 8. Kwa kila moja ya viungo vilivyobaki, uwezekano huu ni 12.5% ​​(1/8).

wakilisha usambazaji kwa picha vigezo random iwezekanavyo kutumia histograms. Katika kesi hii, frequency ya kutokea kwa kila kiunga kwenye sentensi inaonekana wazi:

Kwa hiyo, maandishi yetu yana maneno na viungo vya kipekee, na tulionyesha usambazaji wa uwezekano wa kuonekana kwa kila kiungo katika sentensi kwenye histogram. Ikiwa unafikiri haifai kusumbua na takwimu, endelea. Na labda itaokoa maisha yako.

Kiini cha ufafanuzi

Sasa hebu tuongeze kwenye vipengele vyetu vya maandishi ambavyo huonyeshwa kila wakati, lakini visivyoonyeshwa katika hotuba ya kila siku - mwanzo na mwisho wa sentensi:

Sentensi yoyote ina "mwanzo" na "mwisho" hizi zisizoonekana; hebu tuziongeze kama viungo vya usambazaji wetu:

Wacha turudi kwenye ufafanuzi uliotolewa mwanzoni mwa kifungu:

Mchakato wa Markov - mchakato wa nasibu, ambaye mageuzi baada ya yoyote kuweka thamani parameta ya wakati t haitegemei mageuzi yaliyotangulia t, mradi thamani ya mchakato kwa wakati huu imewekwa.

Mnyororo wa Markov - kesi maalum Mchakato wa Markov, wakati nafasi ya majimbo yake ni tofauti (yaani, si zaidi ya kuhesabiwa).

Kwa hivyo hii inamaanisha nini? Kwa kusema, tunaunda mchakato ambao hali ya mfumo kwa wakati unaofuata inategemea tu hali yake kwa sasa, na haitegemei kwa njia yoyote juu ya majimbo yote yaliyotangulia.

Fikiria kile kilicho mbele yako dirisha, ambayo inaonyesha tu hali ya sasa ya mfumo (kwa upande wetu, ni neno moja), na unahitaji kuamua nini neno linalofuata litategemea tu data iliyotolewa kwenye dirisha hili. Katika mkusanyiko wetu, maneno hufuatana kulingana na muundo ufuatao:

Kwa hivyo, jozi za maneno huundwa (hata mwisho wa sentensi ina jozi yake - maana tupu):

Hebu tupange jozi hizi kwa neno la kwanza. Tutaona kwamba kila neno lina seti yake ya viungo, ambayo katika muktadha wa sentensi yetu unaweza mfuate:

Wacha tuwasilishe habari hii kwa njia nyingine - kwa kila kiunga tunapeana safu ya maneno yote ambayo yanaweza kuonekana kwenye maandishi baada ya kiunga hiki:

Hebu tuangalie kwa karibu. Tunaona kwamba kila kiungo kina maneno ambayo unaweza kuja baada yake katika sentensi. Ikiwa tungeonyesha mchoro hapo juu kwa mtu mwingine, mtu huyo angeweza, kwa uwezekano fulani, kuunda upya wetu ofa ya awali, yaani, mwili.

Mfano. Hebu tuanze na neno "Anza". Ifuatayo, chagua neno "Moja", kwani kulingana na mpango wetu hili ndilo neno pekee linaloweza kufuata mwanzo wa sentensi. Nyuma ya Neno "Moja" pia neno moja tu linaweza kufuata - "samaki". Sasa pendekezo jipya katika toleo la kati linaonekana kama "Samaki mmoja". Zaidi ya hayo hali inakuwa ngumu zaidi - kwa "samaki" kunaweza kuwa na maneno yenye uwezekano sawa wa 25% "mbili", "nyekundu", "bluu" na mwisho wa sentensi "Mwisho". Ikiwa tunadhani kwamba neno linalofuata ni "mbili", ujenzi utaendelea. Lakini tunaweza kuchagua kiungo "Mwisho". Katika kesi hii, kulingana na mpango wetu, sentensi itatolewa kwa nasibu ambayo ni tofauti sana na corpus - "Samaki mmoja".

Tumeiga tu Mchakato wa Markov- kutambuliwa kila neno linalofuata tu kwa misingi ya ujuzi kuhusu moja ya sasa. Ili kuelewa nyenzo kikamilifu, hebu tujenge michoro inayoonyesha utegemezi kati ya vipengee vilivyo ndani ya corpus yetu. Ovals inawakilisha viungo. Mishale inaongoza kwa viungo vinavyowezekana vinavyoweza kufuata neno katika mviringo. Karibu na kila mshale kuna uwezekano ambao kiungo kinachofuata kitatokea baada ya kile cha sasa:

Kubwa! Tumejifunza taarifa muhimu ili kuendelea na kuchambua mifano ngumu zaidi.

Kupanua msingi wa msamiati

Katika sehemu hii ya kifungu tutaunda mfano kulingana na kanuni sawa na hapo awali, lakini katika maelezo tutaacha hatua kadhaa. Ikiwa una matatizo yoyote, rudi kwenye nadharia katika block ya kwanza.

Wacha tuchukue nukuu nne zaidi kutoka kwa mwandishi huyo huyo (pia kwa Kiingereza, haitatuumiza):

"Leo wewe ni wewe. Hiyo ni kweli kuliko kweli. Hakuna aliye hai ambaye ni wewe kuliko wewe."

« Unayo akili kichwani mwako. Una miguu katika viatu vyako. Unaweza kujielekeza katika mwelekeo wowote unaochagua. Uko peke yako."

"Kadiri unavyosoma, ndivyo utakavyojua vitu vingi." Kadiri unavyojifunza zaidi, ndivyo utakavyoenda maeneo mengi zaidi."

"Fikiria kushoto na kufikiri kulia na kufikiri chini na kufikiri juu. Ah, mawazo unaweza fikiria ikiwa utajaribu tu."

Ugumu wa corpus umeongezeka, lakini kwa upande wetu hii ni pamoja - sasa jenereta ya maandishi itaweza kutoa sentensi zenye maana zaidi. Ukweli ni kwamba katika lugha yoyote kuna maneno ambayo yanaonekana katika hotuba mara nyingi zaidi kuliko wengine (kwa mfano, tunatumia preposition "katika" mara nyingi zaidi kuliko neno "cryogenic"). Vipi maneno zaidi katika ushirika wetu (na kwa hivyo utegemezi kati yao), ndivyo habari zaidi jenereta inayo kuhusu ni neno gani linalowezekana kuonekana kwenye maandishi baada ya lile la sasa.

Njia rahisi ya kuelezea hii ni kutoka kwa mtazamo wa programu. Tunajua kwamba kwa kila kiungo kuna seti ya maneno ambayo inaweza kufuata. Na pia, kila neno lina sifa ya idadi ya kuonekana kwake katika maandishi. Tunahitaji njia fulani ya kunasa taarifa hizi zote katika sehemu moja; Kwa kusudi hili, kamusi ya kuhifadhi jozi "(ufunguo, thamani)" inafaa zaidi. Kitufe cha kamusi kitarekodi hali ya sasa ya mfumo, ambayo ni, moja ya viungo vya mwili (kwa mfano, "ya" katika picha hapa chini); na kamusi nyingine itahifadhiwa katika thamani ya kamusi. Katika kamusi iliyohifadhiwa, funguo zitakuwa maneno ambayo yanaweza kuonekana katika maandishi baada ya kiungo cha sasa cha corpus ( "anafikiri" Na "zaidi" inaweza kufuata katika maandishi "ya"), na maadili ni idadi ya kuonekana kwa maneno haya kwenye maandishi baada ya kiungo chetu (neno "anafikiri" inaonekana katika maandishi baada ya neno "ya" 1 wakati, neno "zaidi" baada ya neno "ya"- mara 4):

Soma tena aya iliyo hapo juu mara kadhaa ili kuhakikisha umeielewa sawasawa. Tafadhali kumbuka kuwa kamusi iliyoorodheshwa katika kesi hii ni histogramu sawa; hutusaidia kufuatilia viungo na marudio ya kuonekana kwao katika maandishi kulingana na maneno mengine. Ikumbukwe kwamba hata msingi wa msamiati kama huo ni mdogo sana kwa kizazi sahihi cha maandishi lugha ya asili- inapaswa kuwa na maneno zaidi ya 20,000, au bora zaidi, zaidi ya 100,000. Na hata bora zaidi, zaidi ya 500,000. Lakini hebu tuangalie msingi wa msamiati tulio nao.

Mlolongo wa Markov katika kesi hii umejengwa sawa na mfano wa kwanza - kila neno linalofuata linachaguliwa tu kwa misingi ya ujuzi kuhusu neno la sasa, maneno mengine yote hayazingatiwi. Lakini kutokana na uhifadhi katika kamusi ya data kuhusu maneno ambayo yanaonekana mara nyingi zaidi kuliko wengine, tunaweza kukubali wakati wa kuchagua uamuzi sahihi. Hebu tuangalie mfano maalum:

Zaidi:

Hiyo ni, ikiwa neno la sasa ni neno "zaidi", baada yake kunaweza kuwa na maneno yenye uwezekano sawa wa 25% "vitu" Na "maeneo", na kwa uwezekano wa 50% - neno "hiyo". Lakini uwezekano wote unaweza kuwa sawa:

Fikiria:

Kufanya kazi na Windows

Hadi sasa, tumezingatia tu madirisha ukubwa wa neno moja. Unaweza kuongeza ukubwa wa dirisha ili jenereta ya maandishi itoe sentensi zaidi "zilizothibitishwa". Hii ina maana kwamba dirisha kubwa, kupotoka ndogo kutoka kwa mwili wakati wa kizazi. Kuongeza saizi ya dirisha inalingana na mpito wa mnyororo wa Markov hadi zaidi utaratibu wa juu. Hapo awali, tulijenga mzunguko wa utaratibu wa kwanza; kwa dirisha, maneno mawili yatatoa mzunguko wa pili, tatu itatoa mzunguko wa tatu, na kadhalika.

Dirisha- hii ni data ndani hali ya sasa mifumo inayotumika kufanya maamuzi. Ikiwa tunalingana dirisha kubwa na seti ndogo ya data, basi uwezekano mkubwa tutapokea sentensi sawa kila wakati. Wacha tuchukue msingi wa msamiati kutoka kwa mfano wetu wa kwanza na kupanua dirisha hadi saizi ya 2:

Ugani umemaanisha kuwa kila dirisha sasa lina chaguo moja tu kwa hali inayofuata ya mfumo - haijalishi tunafanya nini, tutapokea kila wakati hukumu sawa, sawa na kesi yetu. Kwa hiyo, ili kujaribu madirisha, na kwa jenereta ya maandishi kurejesha maudhui ya kipekee, hifadhi msingi wa msamiati kutoka kwa maneno 500,000.

Utekelezaji katika Python

Muundo wa data ya Dictogram

Dictogram (dict ni aina ya data ya kamusi iliyojengewa ndani katika Python) itaonyesha uhusiano kati ya viungo na marudio ya kutokea kwao katika maandishi, yaani, usambazaji wake. Lakini wakati huo huo, itakuwa na sifa ya kamusi tunayohitaji - wakati wa utekelezaji wa programu hautategemea kiasi cha data ya uingizaji, ambayo ina maana kwamba tunaunda algorithm yenye ufanisi.

Ingiza Dictogram(dict) ya darasa nasibu: def __init__(self, iterable=None): # Anzisha usambazaji wetu kama kitu kipya class, # ongeza vipengee vilivyopo super(Dictogram, self).__init__() self.types = 0 # idadi ya funguo za kipekee katika self.tokens ya usambazaji = 0 # jumla ya idadi ya maneno yote katika usambazaji ikiwa yanawezekana: self.update( iterable) def update (self, iterable): # Sasisha usambazaji kwa vipengele kutoka kwa data iliyopo # inayoweza kutekelezeka kwa ajili ya kipengee kinachoweza kutekelezeka: ikiwa bidhaa yenyewe: self += 1 self.tokens += 1 kingine: self = 1 binafsi. type += 1 self.tokens += 1 def count(self, item): # Rejesha thamani ya kaunta ya bidhaa, au 0 ikiwa kipengee kibinafsi: rudisha ubinafsishaji 0 def return_random_word(self): random_key = random.sample(self, 1) # Njia nyingine: # random .choice(histogram.keys()) return random_key def return_weighted_random_word(self): # Tengeneza nambari bandia-nasibu kati ya 0 na (n-1), # ambapo n ni jumla ya idadi ya maneno. random_int = random.randint(0, self.tokens-1 ) index = 0 list_of_keys = self.keys() # print "random index:", random_int for i in range(0, self.types): index += self] # faharasa ya kuchapisha ikiwa(index > random_int): # orodha ya_ya_funguo za kuchapisha [i] orodha_ya_funguo_zorejeshwa[i]

Mjenzi wa muundo wa Dictogram anaweza kupitisha kitu chochote ambacho kinaweza kurudiwa tena. Vipengele vya kitu hiki vitakuwa maneno ya kuanzisha Dictogram, kwa mfano, maneno yote kutoka kwa kitabu. Katika kesi hii, tunahesabu vipengele ili kufikia yoyote kati yao hatuhitaji kupitia seti nzima ya data kila wakati.

Pia tulifanya kazi mbili kurudisha neno nasibu. Kazi moja huchagua ufunguo wa random katika kamusi, na nyingine, kwa kuzingatia idadi ya matukio ya kila neno katika maandishi, inarudi neno tunalohitaji.

Muundo wa mnyororo wa Markov

kutoka kwa histograms import Dictogram def make_markov_model(data): markov_model = dict() for i in range(0, len(data)-1): if data[i] in markov_model: # Ongeza tu kwa markov_model ya usambazaji iliyopo].sasisha( ]) mwingine: markv_model] = Dictogram() rudisha markv_model

Katika utekelezaji hapo juu, tuna kamusi inayohifadhi madirisha kama ufunguo katika jozi ya "(ufunguo, thamani)" na usambazaji kama thamani katika jozi hiyo.

Agizo la Nth muundo wa mnyororo wa Markov

from histograms import Dictogram def make_higher_order_markov_model(order, data): markov_model = dict() for i in range(0, len(data)-order): # Create a window window = tuple(data) # Ongeza kwenye kamusi ikiwa dirisha in. markov_model: # Ambatanisha kwa usambazaji uliopo markv_model.sasisha() mwingine: markov_model = Dictogram() return markkov_model

Sawa sana na mnyororo wa kwanza wa Markov, lakini katika kesi hii tunahifadhi msafara wa magari kama ufunguo katika jozi ya "(ufunguo, thamani)" katika kamusi. Tunatumia badala ya orodha, kwani tuple inalindwa kutokana na mabadiliko yoyote, na hii ni muhimu kwetu - baada ya yote, funguo katika kamusi haipaswi kubadilika.

Uchanganuzi wa mfano

Mkuu, tumetekeleza kamusi. Lakini tunawezaje kuzalisha maudhui kulingana na hali ya sasa na hatua ya kuelekea hali inayofuata? Wacha tupitie mfano wetu:

Kutoka kwa histograms import Dictogram import random from collections import deque import re def generate_random_start(model): # Ili kutoa neno lolote la kuanzia, toa maoni kwenye mstari: # return random.choice(model.keys()) # Kuzalisha neno la kuanzia "sahihi" , tumia nambari iliyo hapa chini: # Sahihi maneno ya awali- hizi ni zile ambazo zilikuwa mwanzo wa sentensi katika jumla ikiwa "END" katika muundo: seed_word = "END" huku seed_word == "END": seed_word = model["END"].return_weighted_random_word() rudisha seed_word return random. choice(model .keys()) def generate_random_sentence(urefu, markov_model): current_word = generate_random_start(markov_model) sentensi = kwa i katika safu(0, urefu): current_dictogram = markov_model random_weighted_word = current_dictogram.return_weighted_random_word.return_weighted_random_word. (neno_la_sasa) sentensi = sentensi.capitalize() rudisha " ".jiunge(sentensi) + "." kurudisha hukumu

Nini kinafuata?

Jaribu kufikiria ni wapi unaweza kutumia jenereta ya maandishi kulingana na minyororo ya Markov mwenyewe. Usisahau tu kwamba jambo muhimu zaidi ni jinsi unavyochanganua mfano na ni vikwazo gani maalum unavyoweka kwenye kizazi. Mwandishi wa nakala hii, kwa mfano, wakati wa kuunda jenereta ya tweet, alitumia dirisha kubwa, alipunguza yaliyomo kwa herufi 140, na alitumia maneno "sahihi" tu kuanza sentensi, ambayo ni, zile ambazo zilikuwa mwanzo wa sentensi katika. shirika.