Help us understand the problem. What is going on with this article?

[Pythonで遊ぼう] 文章自動生成をめざす ~文章自動生成の完成~

はじめに

文章自動生成をめざす、三回目となります。今回は文章生成のための関数を作っていきます。コードとしては長くなります。順番にやっていきましょう。

コード部分

テキストデータの準備をする

ではコードの話となります。まず使うものがこちらになります。

import re
from janome.tokenizer import Tokenizer
from tqdm import tqdm
from collections import Counter
from collections import defaultdict
import random
t = Tokenizer()

テキストを準備して読み込ませます。さらにテキストの内容をきれいにしておきます。この辺は前回記事でやった通りです。

a = open('test.txt', 'r', encoding = "utf-8") 
original_text = a.read()
#print(original_text) #文書を表示

first_sentence = '「Pythonの説明。」'
last_sentence = 'Pythonという英単語が意味する爬虫類のニシキヘビがPython言語のマスコットやアイコンとして使われている。'
#テキストデータを整理する。
_, text = original_text.split(first_sentence)
text, _ = text.split(last_sentence)
text = first_sentence + text + last_sentence

text = text.replace('!', '。') #!や?を。に変える。全角半角に気を付ける
text = text.replace('?', '。')
text = text.replace('(', '').replace(')', '') #()を削除する。
text = text.replace('\r', '').replace('\n', '') # テキストデータの改行で表示される\nを削除
text = re.sub('[、「」?]', '', text) 
sentences = text.split('。') #。で文章を一文単位に分割
print('文字数:', len(sentences))
sentences[:10] #10文を表示

文章を分解していく

一文ごとに分解します。

start = '__start__'  # 文の開始マーク
fin = '__fin__'  # 文の終了

def get_three_words_list(sentence):  #文章を3単語の組にして返す
    t = Tokenizer()
    words = t.tokenize(sentence, wakati=True)
    words = [start] + words + [fin]
    three_words_list = []
    for i in range(len(words) - 2):
        three_words_list.append(tuple(words[i:i+3]))
    return three_words_list

three_words_list = []
for sentence in tqdm(sentences):
    three_words_list += get_three_words_list(sentence)

three_words_count = Counter(three_words_list)
len(three_words_count) 

単語同士のつながりや重みを付ける

#マルコフ連鎖
def generate_markov_dict(three_words_count):
    markov_dict = {}
    for three_words, count in three_words_count.items():
        two_words = three_words[:2]  #前半2つの単語と次の単語に分割
        next_word = three_words[2]
        if two_words not in markov_dict: #辞書に存在しない場合は空データを生成
            markov_dict[two_words] = {'words': [], 'weights': []}
            markov_dict[two_words]['words'].append(next_word)  #次の単語と回数を追加
            markov_dict[two_words]['weights'].append(count)
    return markov_dict

markov_dict = generate_markov_dict(three_words_count)
markov_dict
def get_first_words_weights(three_words_count):
    first_word_count = defaultdict(int)

    for three_words, count in three_words_count.items():
        if three_words[0] == start:
            next_word = three_words[1]
            first_word_count[next_word] += count

    words = []  #単語と重み(出現回数)を格納するリスト
    weights = []
    for word, count in first_word_count.items():
        words.append(word)  #単語と重みをリストに追加
        weights.append(count)
    return words, weights

get_first_words_weights(three_words_count)
markov_dict = generate_markov_dict(three_words_count)
print(len(markov_dict))
first_words, first_weights = get_first_words_weights(three_words_count)
print(len(first_words))
def get_first_words_weights(three_words_count):
    first_word_count = defaultdict(int)

    for three_words, count in three_words_count.items():
        if three_words[0] == start:
            next_word = three_words[1]
            first_word_count[next_word] += count

    words = []  #単語と重み(出現回数)を格納するリスト
    weights = []
    for word, count in first_word_count.items():
        words.append(word)  #単語と重みをリストに追加
        weights.append(count)
    return words, weights

get_first_words_weights(three_words_count)
def get_first_words_weights(three_words_count):
    first_word_count = defaultdict(int)  #値がint型のdefaultdictを作成
    for three_words, count in three_words_count.items():
        if three_words[0] == start:  #startで始まるもののみを取り出す
            next_word = three_words[1]
            first_word_count[next_word] += count # 出現回数を加算
    return first_word_count

get_first_words_weights(three_words_count)

文章自動生成をする

def generate_text(fwords, fweights, markov_dict):
    first_word = random.choices(fwords, weights=fweights)[0]  #最初の単語を取得
    generate_words = [start, first_word]  #文章生成用に単語を格納するリスト
    while True:
        pair = tuple(generate_words[-2:])  #最後の2つの単語を取得
        words = markov_dict[pair]['words']  #次の単語と重みのリストを取得
        weights = markov_dict[pair]['weights']
        next_word = random.choices(words, weights=weights)[0]  #次の単語を取得
        if next_word == fin:  #文章が終了した場合はループを抜ける
            break
        generate_words.append(next_word)
    return ''.join(generate_words[1:])  #単語から文章を作成

生成開始!

for l in range(3):
    sentence = generate_text(first_words, first_weights, markov_dict)
    print(sentence)

結果はこれです。
2020-02-19.png

反省と注意

( ゚Д゚) 原文ママがでてしまった...
絶対的に元のテキスト量が少ないです。
注意点) 私が実行してみたところPCのスペック不足かうまく生成されないことがありました。理由は不明。あと大量のテキストだと時間がかかる。

雑談

これで文章の自動生成はできました。「文章自動生成をめざす」はこれで以上となります。改善点はいくつかありますが、やはり元となる文章量や特徴が足りていないです。そのためどうしても原文ママの文章ができあがってしまうことが多々あります。なのでこの例では面白みがない文章となってしまったのが個人的に残念な点です。多少直せたらまた記事にします。

このコードは書籍とそのサンプルコードをもとにつくっています。(本の名前は忘れてしまいました。)その際行った、太宰治.人間失格をもとにできあがった文章も載せておきます。
2020-02-19 (1).png

AEC
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした