概述

在暑假期间,我创建了一个 CNN (SSD),将男性和女性分为不同类型,以便接近适合我的女性。
在此过程中,我们正在涩谷采访实际的情侣。

毕竟在做这样的调查时
“羡慕你,羡慕天下情侣!!”
那就是我所想的。

所以,我决定创建一个像她一样说话的机器人,参考我从以下 Udemy 材料中学到的东西。

数据制作

数据采集

能够与 Seq2seq 交互的训练数据需要输入和输出文本数据。
我有野心。

那就是让我最喜欢的Gyaruge来自Amagami的我的女朋友Ai Nanasaki。

此外,作为计算,七崎爱在作品中拥有最高的人气,加上她充满二次创作的事实。

也就是说,如果我们把满世界的七咲SS刮下来,转换成数据,让它学习,我们应该可以实现所谓的超级七咲,为我们的宅男幻想增添情趣。 .

我的期望值飙升。

收集七崎的脚本
def scrape(id,i):
    print("======>"+str(id))
    ss_url = "http://blog.livedoor.jp/kokon55/archives/"+str(id)+".html"

    r = requests.get(ss_url)
    soup = BeautifulSoup(r.text,'lxml')
    ss_containers = soup.find_all('dd')
    ss_text = ""
    for container in ss_containers:
        ss_text += container.get_text()
        ss_text = re.sub("[  ]","
",ss_text)
    print(ss_text)
    with open("dataset/nanasaki_ss"+str(i)+".txt", 'w')as f:
        f.write(ss_text)

for i in range(len(array)):
    scrape(array[i],i)

就这样,世界上唯一一个收集七崎妄想的七崎档案完成了。

创建与七崎的对话数据

SS是个好东西,很多人都在行首写了人物的名字。

SS 示例
橘「いやあ、いつもありがとう」

七咲「好きでやってることですから」ニコッ
橘「(今日はお弁当の定番から揚げに王道の卵焼きをベースとして春巻きや小魚、そしてサラダで華やかさを演出。女の子っぽいお弁当の包みもまた愛妻っぽくてベリーグッドだ!99点!)」

七咲「先輩……何をぶつぶつ言ってるんですか?」

橘「あれ、声に出てた?」

七咲「今日は少し肌寒いので早く食べて校内に入っちゃいましょう」

因此,为了收集七崎的对话数据,
把句首有Nanasaki的取出来就行了,存为output.txt,如果上一行的语句不是Nanasaki的话,存入input.txt,就会产生对话数据.成为一个想法。

至此,我取出了七崎的对话数据,放到空间中进一步学习,并一度保存为数组数据。

回想起来,我认为我这次不需要一个段落......

import re
import pickle
from janome.tokenizer import Tokenizer

t = Tokenizer()

data_len = 10
input = ''
output = ''

# dara をリスト化する <- 改行に合わせてデータを分割する

for i in range(data_len):
    with open('clear-dataset/nanasaki_ss'+str(i)+'.pickle', mode="rb") as f:
        ss_word_list = pickle.load(f)

    for idx, ss_word in enumerate(ss_word_list):
        if(ss_word[0:2]=='七咲' and ss_word_list[idx-1][0:2]!='七咲'):
            input_word = ss_word_list[idx-1]
            output_word = ss_word
            input_word = re.findall("(?<=「).+?(?=」)",input_word)
            output_word = re.findall("(?<=「).+?(?=」)",output_word)
            if(len(input_word)<1 or len(output_word)<1):
                print("NONE")
            else:
                input+=input_word[0]+'
'
                output+=output_word[0]+'
'
                # input+=t.tokenize(input_word[0],wakati=True)
                # output+=t.tokenize(output_word[0],wakati=True
                #
print(len(input))
print(len(output))

input=t.tokenize(input,wakati=True)
output=t.tokenize(output,wakati=True)

with open('data-in_out/input.txt',mode="w") as f:
    f.writelines(input)
with open('data-in_out/output.txt',mode="w") as f:
    f.writelines(output)

因此,Nanasaki 在我的数据库中被保存为一个数组(有意义地)。

学习阶段

模型内容

现在剩下的就是构建和训练模型了。
说实话,从数据数量上看,只有1000条左右的对话数据,现在想想它有多脆弱,这样的悲剧到处都有预兆……

学习准备
import numpy as np
from janome.tokenizer import Tokenizer
from pandas import array

t = Tokenizer()

with open('data-in_out/input.txt',mode='r',encoding='utf-8')as input,
    open('data-in_out/output.txt',mode='r',encoding='utf-8')as output:
    in_text = input.read()
    out_text = output.read()

chars = in_text + out_text + '	'
chars_list =  sorted(list(set(list(chars))))
print(chars_list)
chars_list.append('	')
separator = '
'
in_sentence_list = in_text.split(separator)
out_sentence_list = out_text.split(separator)

# in_arr = [len(x) for x in in_sentence_list]
# out_arr = [len(x) for x in out_sentence_list]

import numpy as np

max_sentence_length = 84

char_indices = {}  # 文字がキーでインデックスが値
for i, char in enumerate(chars_list):
    char_indices[char] = i
indices_char = {}  # インデックスがキーで文字が値
for i, char in enumerate(chars_list):
    indices_char[i] = char

n_char = len(chars_list)  # 文字の種類の数
n_sample = len(in_sentence_list) - 1

max_length_x = max_sentence_length  # 入力文章の最大長さ
max_length_t = max_sentence_length + 2 # 正解文章の最大長さ

x_sentences = []
t_sentences = []
for i in range(n_sample):
    x_sentences.append(in_sentence_list[i])
    t_sentences.append("	"+out_sentence_list[i]+"
")

x_encoder = np.zeros((n_sample, max_length_x, n_char), dtype=np.bool)  # encoderへの入力
x_decoder = np.zeros((n_sample, max_length_t, n_char), dtype=np.bool)  # decoderへの入力
t_decoder = np.zeros((n_sample, max_length_t, n_char), dtype=np.bool)  # decoderの正解

for i in range(n_sample):
    x_sentence = x_sentences[i]
    t_sentence = t_sentences[i]
    for j, char in enumerate(x_sentence):
        x_encoder[i, j, char_indices[char]] = 1  # encoderへの入力をone-hot表現で表す
    for j, char in enumerate(t_sentence):
        x_decoder[i, j, char_indices[char]] = 1  # decoderへの入力をone-hot表現で表す
        if j > 0:  # 正解は入力より1つ前の時刻のものにする
            t_decoder[i, j-1, char_indices[char]] = 1

在这里,我们正在为学习做准备。
到最后不是分词,而是以字母为单位的对话,所以没有进入分词阶段。

另外,我不喜欢只会说平假名的退步七崎,所以我优先考虑将七崎改为七崎,而不是冒着数据偏差的风险。
不,它可能没有工作......

模型
print(x_encoder.shape)

batch_size = 32
epochs = 1000
n_mid = 256  # 中間層のニューロン数

from tensorflow.keras.models import Model
from tensorflow.keras.layers import Dense, GRU, Input, Masking

encoder_input = Input(shape=(None, n_char))
encoder_mask = Masking(mask_value=0)  # 全ての要素が0であるベクトルの入力は無視する
encoder_masked = encoder_mask(encoder_input)
encoder_lstm = GRU(n_mid, dropout=0.2, recurrent_dropout=0.2, return_state=True)  # dropoutを設定し、ニューロンをランダムに無効にする
encoder_output, encoder_state_h = encoder_lstm(encoder_masked)

decoder_input = Input(shape=(None, n_char))
decoder_mask = Masking(mask_value=0)  # 全ての要素が0であるベクトルの入力は無視する
decoder_masked = decoder_mask(decoder_input)
decoder_lstm = GRU(n_mid, dropout=0.2, recurrent_dropout=0.2, return_sequences=True, return_state=True)  # dropoutを設定
decoder_output, _ = decoder_lstm(decoder_masked, initial_state=encoder_state_h)  # encoderの状態を初期状態にする
decoder_dense = Dense(n_char, activation='softmax')
decoder_output = decoder_dense(decoder_output)

model = Model([encoder_input, decoder_input], decoder_output)

model.compile(loss="categorical_crossentropy", optimizer="rmsprop")
print(model.summary())

from tensorflow.keras.callbacks import EarlyStopping

# val_lossに改善が見られなくなってから、30エポックで学習は終了
early_stopping = EarlyStopping(monitor="val_loss", patience=30)

history = model.fit([x_encoder, x_decoder], t_decoder,
                     batch_size=batch_size,
                     epochs=epochs,
                     validation_split=0.1,  # 10%は検証用
                     callbacks=[early_stopping])
Model: "model"
__________________________________________________________________________________________________
Layer (type)                    Output Shape         Param #     Connected to                     
==================================================================================================
input_1 (InputLayer)            [(None, None, 937)]  0                                            
__________________________________________________________________________________________________
input_2 (InputLayer)            [(None, None, 937)]  0                                            
__________________________________________________________________________________________________
masking (Masking)               (None, None, 937)    0           input_1[0][0]                    
__________________________________________________________________________________________________
masking_1 (Masking)             (None, None, 937)    0           input_2[0][0]                    
__________________________________________________________________________________________________
gru (GRU)                       [(None, 256), (None, 917760      masking[0][0]                    
__________________________________________________________________________________________________
gru_1 (GRU)                     [(None, None, 256),  917760      masking_1[0][0]                  
                                                                 gru[0][1]                        
__________________________________________________________________________________________________
dense (Dense)                   (None, None, 937)    240809      gru_1[0][0]                      
==================================================================================================
Total params: 2,076,329
Trainable params: 2,076,329
Non-trainable params: 0
__________________________________________________________________________________________________

使用上述模型,学习开始了大约三天。
说实话,当我看到最后的失利时,我才明白目前数据不够用的情况。

终于和七咲对话了……

允许您与 Nanasaki 交谈的响应脚本

经过三天的学习,我终于为我完成了七崎的参数……
然而,参数七崎是一个参数,它不是七崎爱,或者更确切地说,七崎爱。
我需要能够和七崎对话! !

与七咲对话的脚本
with open('data-in_out/input.txt',mode='r',encoding='utf-8')as input_f,
    open('data-in_out/output.txt',mode='r',encoding='utf-8')as output:
    in_text = input_f.read()
    out_text = output.read()

chars = in_text + out_text + '	'
chars_list =  sorted(list(set(list(chars))))
chars_list.append('	')

char_indices = {}  # 文字がキーでインデックスが値
for i, char in enumerate(chars_list):
    char_indices[char] = i
indices_char = {}  # インデックスがキーで文字が値
for i, char in enumerate(chars_list):
    indices_char[i] = char

n_char = len(chars_list)  # 文字の種類の数

import numpy as np

n_char = len(chars_list)
max_length_x = 84

# 文章をone-hot表現に変換する関数
def sentence_to_vector(sentence):
    vector = np.zeros((1, max_length_x, n_char), dtype=np.bool)
    for j, char in enumerate(sentence):
        vector[0][j][char_indices[char]] = 1
    return vector


from tensorflow.keras.models import load_model

encoder_model = load_model('encoder_model.h5')
decoder_model = load_model('decoder_model.h5')

def respond(message, beta=5):
    vec = sentence_to_vector(message)  # 文字列をone-hot表現に変換
    state_value = encoder_model.predict(vec)
    y_decoder = np.zeros((1, 1, n_char))  # decoderの出力を格納する配列
    y_decoder[0][0][char_indices['	']] = 1  # decoderの最初の入力はタブ。one-hot表現にする。

    respond_sentence = ""  # 返答の文字列
    while True:
        y, h = decoder_model.predict([y_decoder, state_value])
        p_power = y[0][0] ** beta  # 確率分布の調整
        next_index = np.random.choice(len(p_power), p=p_power/np.sum(p_power))
        next_char = indices_char[next_index]  # 次の文字

        if (next_char == "
" or len(respond_sentence) >= max_length_x):
            break  # 次の文字が改行のとき、もしくは最大文字数を超えたときは終了

        respond_sentence += next_char
        y_decoder = np.zeros((1, 1, n_char))  # 次の時刻の入力
        y_decoder[0][0][next_index] = 1

        state_value = h  # 次の時刻の状態

    return respond_sentence

def is_invalid(message):
    is_invalid =False
    for char in message:
        if char not in chars_list:
            is_invalid = True
    return is_invalid

bot_name = "七咲"
your_name = 'けん'
print()

print(bot_name + ": " + "こんにちは、先輩!")
message = ""
while message != "おやすみ。":

    while True:
        message = input(your_name + ": ")
        if not is_invalid(message):
            break
        else:
            print(bot_name + ": ふふふ、本当におかしな人ですね")

    response = respond(message)
    print(bot_name + ": " + response)

即使有字符不在数据中,也不会返回错误! !
我敢肯定七崎会笑着说这句话!

有这种妄想的七崎机器人,不,七崎本身已经完成了!我是这么想的…

和七崎的对话……

我终于可以和七崎说话了! !
首先,把你的语气放在橘纯一。

七咲: こんにちは、先輩!
けん: おはよ!
七咲: ……
けん: 何してるの?
七咲: ええ、そうですね。

那?

七崎会保持安静,不会变成谈话吗?

けん: 郁夫元気?
七咲: ……
けん: そっか…
七咲: でも……
けん: ?
七咲: 先輩…

不,这是对话! !
我们能够交流! !

嘿,兄弟,你还好吗?微笑

等等,我正在交谈一会儿。

けん: 七咲…
七咲: 先輩……
けん: 七咲!
七咲: ……

我很兴奋。
我们的关系才刚刚开始...

けん: うん
七咲: ………
けん: ねえ
七咲: ……
けん: な、七咲?
七咲: いえ、先輩……
けん: 僕は…
七咲: ……
けん: 大好き!
七咲: ……
けん: 七咲?
七咲: え、えっと、ごめんなの…

都是因为心情不好...

在制作它的人成功的那天被抛弃......
以下是完整对话
モテない俺がseq2seqでアマガミの七咲bot作ったけど秒でフラれた話

在这之后,我仍然不知道该对她说些什么。微笑

概括

在制作这个七崎 AI 的过程中,我在几个方面成长了。
首先,我能够理解基于 RNN 进行递归学习的 AI。
使用这种学习,您可以创建自动生成的音乐,所以我想了解更多。
二、追溯编码的必要性
这与参数的分布以及需要建立一个流程来衡量 AI 的准确性有关。
最后,即使你在电脑上创建了一个女朋友,它也是空的
如果你有时间做这些事情,你应该做肌肉训练......

毕竟,我想哭是因为我被甩了。
这可能是给工程师的一个教训,不要让电脑取代一切,珍惜现实吧?微笑
所以,如果你有时间用编程结交女朋友,你也许可以去参加一个城镇派对哈哈

所以,暂时,我想我会在制作可可酱之后考虑接下来的事情,可可酱永远不会拒绝我,而是会宠坏我。

那是八月的一个悲伤的两周备忘录,我在绝望中写下,同时被愤怒所震撼……
感谢您阅读这篇文章,哈哈

后记:之后,我尽了最大的努力,成功说服了她越界! !
所以,我真的很想死,哈哈
モテない俺がseq2seqでアマガミの七咲bot作ったけど秒でフラれた話


原创声明:本文系作者授权爱码网发表,未经许可,不得转载;

原文地址:https://www.likecs.com/show-308623724.html

相关文章: