LoginSignup
1
1

More than 1 year has passed since last update.

なろうで小説を書いてみよう2!

Last updated at Posted at 2022-04-17

前回の改善版です!

まずはスクリプトから

train.py
# coding=utf-8
# Copyright 2021 rinna Co., Ltd.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.


import math
import random
import time
import argparse
import os

import numpy as np
import torch
import torch.distributed as dist
import torch.optim as optim
import torch.multiprocessing as mp
import torch.cuda.amp as amp
from torch.nn import functional as F
from torch.nn.parallel import DistributedDataParallel as DDP
from torch.utils.tensorboard import SummaryWriter
from torch.utils.data import RandomSampler
from torch.utils.data.distributed import DistributedSampler
from transformers import GPT2LMHeadModel, GPT2Config
from transformers import T5Tokenizer

from lr_scheduler import get_linear_schedule_with_warmup
from data_source import DataSource, collate_fn
from itertools import chain

import sys
import os
sys.path.append(os.path.dirname(os.path.abspath(__file__)))

def str2bool(v):
    return v.lower() in ('true', '1', "True")


def mp_print(text):
    print(text)


#ここについて、2文ずつ取って保存する形式に変更
def load_docs_from_filepath(filepath, tokenizer):
    docs = []
    with open(filepath, encoding="utf-8") as f:
        lines = f.readlines()
        doc = []
        for i, line in enumerate(lines):
            line = line.strip()
            if line == "" or len(lines) == i-1:
                if len(doc) > 0:
                    if len(doc) >= config.n_cutoff_sentences:
                        for i in range((len(doc)-config.n_cutoff_sentences) //  5 + 2):
                            start_idx = max(0, i * 5 -1)
                            docs.append(list(chain(*doc[start_idx : start_idx + config.n_cutoff_sentences])))
                            print(f"num tokens {len(docs[-1])}")
                    else:
                        docs.append(list(chain(*doc)))
                        print(f"num tokens {len(docs[-1])}")
                doc = []
            else:
                sent = line
                tokens = tokenizer.tokenize(sent)
                token_ids = tokenizer.convert_tokens_to_ids(tokens)
                if len(token_ids) > 0:
                    doc.append(token_ids)
    return docs


def forward_step(model, tokenizer, batch_data):
    max_seq_len = max([len(seq) for seq in batch_data])

    # padding input sequences
    batch_data = [seq + [tokenizer.pad_token_id]*(max_seq_len-len(seq)) for seq in batch_data]

    # convert to tensors
    batch_tensor = torch.LongTensor(batch_data).to(model.device)

    # get inputs and outputs
    input_ids = batch_tensor[:, :-1].contiguous()
    output_ids = batch_tensor[:, 1:].contiguous()

    # forward
    gpt2_outputs = model(input_ids=input_ids, return_dict=True)
    loss = F.cross_entropy(
        gpt2_outputs["logits"].view(-1, len(tokenizer)),
        output_ids.view(-1),
        ignore_index=tokenizer.pad_token_id,
        reduction="mean"
    )
    with torch.no_grad():
        ppl = loss.exp()

    return loss, ppl


def train(config):
    # set random seeds
    torch.manual_seed(config.seed)
    torch.cuda.manual_seed_all(config.seed)
    random.seed(config.seed)
    np.random.seed(config.seed)

    # multi-gpu init
    if torch.cuda.is_available():
        DEVICE = torch.device("cuda:1")
    else:
        DEVICE = torch.device("cpu")

    # build tokenizer
    tokenizer = T5Tokenizer.from_pretrained(config.model_name)
    tokenizer.do_lower_case = True


    # build model
    model = GPT2LMHeadModel.from_pretrained(config.model_name)
    model = model.to(DEVICE)

    # load model from checkpoint
    if config.checkpoint_path:
        mp_print("checkpoint path: {}".format(config.checkpoint_path))
        checkpoint = torch.load(config.checkpoint_path, map_location=model.device)
        model.load_state_dict(checkpoint["model"])
        model.tie_weights()  # NOTE: don't forget to tie weights after loading weights

    # use mixed precision
    if config.use_amp:
        scaler = amp.GradScaler()


    # build optimizer
    param_optimizer = list(model.named_parameters())
    no_decay = ['bias', 'ln']   # no decay for bias and LayerNorm (ln)
    optimizer_grouped_parameters = [
        {
            'params': [p for n, p in param_optimizer if not any(nd in n for nd in no_decay)],
            'weight_decay': config.l2_penalty},
        {
            'params': [p for n, p in param_optimizer if any(nd in n for nd in no_decay)],
            'weight_decay': 0.0
        }
    ]
    optimizer = optim.AdamW(
        optimizer_grouped_parameters,
        lr=config.init_lr,
        betas=(0.9, 0.95),
        eps=1e-8,
        weight_decay=config.l2_penalty
    )

    # build lr scheduler
    lr_scheduler = get_linear_schedule_with_warmup(
        optimizer=optimizer,
        num_warmup_steps=config.n_warmup_steps,
        num_training_steps=config.n_training_steps,
    )

    # init environment or load from checkpoint
    if config.checkpoint_path:
        if config.resume_training:
            mp_print("loading optimizer state dict...")
            optimizer.load_state_dict(checkpoint["optimizer"])
            mp_print("recovering lr scheduler...")
            lr_scheduler.load_state_dict(checkpoint["lr_scheduler"])
            mp_print("recovering others...")
            n_step = checkpoint["n_step"]
            start_n_epoch = checkpoint["n_epoch"]
        else:
            n_step = 0
            start_n_epoch = 0
        OUTPUT_FILEID = checkpoint["output_fileid"]
        del checkpoint
        torch.cuda.empty_cache()
    else:
        n_step = 0
        start_n_epoch = 0

        # names
        OUTPUT_FILEID = "{}.seed_{}.{}".format(
            os.path.basename(config.model_name),
            config.seed,
            config.file_name
        )


    for epoch_idx in range(start_n_epoch, config.n_epochs):
        train_lines = load_docs_from_filepath(config.file_name, tokenizer)
        train_data_source = DataSource(config, tokenizer, train_lines, "train", randomize=True)
        # single gpu or cpu
        train_data_sampler = RandomSampler(
            train_data_source,
            replacement=False
        )
        train_dataloader = torch.utils.data.DataLoader(
            train_data_source,
            batch_size=config.batch_size,
            sampler=train_data_sampler,
            num_workers=0,
            collate_fn=collate_fn,
            pin_memory=True
        )


        for batch_data in train_dataloader:
            try:
                n_step += 1

                # stop if reaches the maximum tranining step
                if n_step >= config.n_training_steps:
                    break

                # forward
                model.train()
                with amp.autocast():
                    loss, ppl = forward_step(model, tokenizer, batch_data)

            # update statisitcs
                print({"ppl": ppl.item(), "loss": loss.item()})

                # backward
                loss /= config.n_accum_steps
                if config.use_amp:
                    scaler.scale(loss).backward()
                else:
                    loss.backward()
                del loss
                torch.cuda.empty_cache()

                if n_step % config.n_accum_steps == 0:
                    # clip gradient
                    if config.max_grad_norm > 0.0:
                        if config.use_amp:
                            scaler.unscale_(optimizer)
                        torch.nn.utils.clip_grad_norm_(model.parameters(), config.max_grad_norm)

                    # update model parameters
                    if config.use_amp:
                        scaler.step(optimizer)
                        scaler.update()
                    else:
                        optimizer.step()

                    # zero gradients
                    optimizer.zero_grad()


                    # evaluation on dev dataset
                    if n_step > 0 and n_step % config.validate_after_n_step == 0:

                        # forward
                        with torch.no_grad():
                            model.eval()


                        # Save model if it has better monitor measurement
                        if config.save_model:
                            print("SAVE MODEL!")
                            model_to_save = model.module if hasattr(model, 'module') else model

                            # save current model
                            checkpoint = {
                                "model": model_to_save.state_dict(),
                                "optimizer": optimizer.state_dict(),
                                "lr_scheduler": lr_scheduler.state_dict(),
                                "n_epoch": epoch_idx,
                                "n_step": n_step,
                                "output_fileid": OUTPUT_FILEID,
                            }
                            torch.save(
                                checkpoint,
                                f"./{OUTPUT_FILEID}.checkpoint"
                            )



                    # decay learning rate
                    lr_scheduler.step()
            except RuntimeError:
                print("RuntiimeError!!!!!!")


if __name__ == "__main__":
    parser = argparse.ArgumentParser()

    # training
    parser.add_argument("--seed", type=int, default=42, help="random initialization seed")
    parser.add_argument("--batch_size", type=int, default=1, help="batch size for training. 20 for xsmall; 8 for small; 3 for medium.")
    parser.add_argument("--n_training_steps", type=int, default=4e6, help="number of maximum training steps. 1.6e6 for xsmall; 4e6 for small; 1.3e7 for medium.")
    parser.add_argument("--n_epochs", type=int, default=10, help="number of maximum training epochs")
    parser.add_argument("--n_warmup_steps", type=int, default=2e3, help="number of warmup steps")
    parser.add_argument("--max_seq_len", type=int, default=1024, help="maximum input sequence length")
    parser.add_argument("--n_accum_steps", type=int, default=8, help="number of gradient accumulation steps. 3 for xsmall; 8 for small; 20 for medium.")

    # mixed precision
    parser.add_argument("--use_amp", type=str2bool, default=True, help="use mixed precision for training")

    # optimizer
    parser.add_argument("--l2_penalty", type=float, default=0.01, help="l2 penalty")
    parser.add_argument("--init_lr", type=float, default=6e-4, help="peak learning rate; 7e-4 for xsmall; 6e-4 for small; 3e-4 for medium.")
    parser.add_argument("--max_grad_norm", type=float, default=1.0, help="gradient clipping threshold")

    # management
    parser.add_argument("--checkpoint_path", help="path to saved checkpoint file")
    parser.add_argument("--resume_training", type=str2bool, default=False, help="resume training from checkpoint or not")
    parser.add_argument("--save_model", type=str2bool, default=True, help="save model to checkpoint or not")
    parser.add_argument("--check_loss_after_n_step", type=int, default=1e2, help="print loss after every this number of steps")
    parser.add_argument("--validate_after_n_step", type=int, default=5e3, help="validate model after every this number of steps")
   parser.add_argment("n_cutoff_sentences", type=int, default=15)

    # customize args
    parser.add_argument("--model_name", type=str, default="rinna/japanese-gpt2-medium")
    parser.add_argument("--file_name", type=str, default='n3170ed.txt')
    parser.add_argument("n_cutoff_sentences", type=int, default=15)

    config = parser.parse_args()

    train(config)


中身はかなり改造しています。まあ、コードリーディング頑張って!前回と違う点は

train.py
tokenzier.from_pretrained(config.model_name)
model.from_pretrained(config.model_name)

として、"rinna/japaneze-gpt2-medium"を読み込んでいること!

やっぱり、Fine-Tuningは素晴らしい!

テキストは前回と同様にしてスクレイピングした

願わくばこの手に幸福を

karia.jpg download.jpg

カリアがマジでかわいい!

生成文章!

Prompt > 願わくば (単語制限200で10回分をつなげたもの)

願わくば
、その身に祝福が満ち溢れることを。髪の毛先から足の爪に至るまで、とてもとても清廉な輝きが。 だから、こそ。エルフであり、魔性の者であり。そうして種族的に最も妖精に近しい己こそが、其れを幸福とするのだと、エルディスはそう信じている。 そうして、それが真実であるならば、エルディスにとって己の幸福などどうでも良いはずだ。 ああいや、嘘だ。きっと、あの式典の前夜、式典の前夜にを借りて軽く聞いていただけだ。それは他でもない、己が歯向かったのが気にくわなかったからなのだ。 だが、其れは本当の事だろうか。もしかすると、あの式典は虚偽ではないのではないだろうか。 此れは紋章教とガザリアの合同会議であったのだから、別段、俺が口に出したわけじゃない。むしろ他勢力との会議だというのだから、それこそ去念に近い意味を持って、
鉄鋼姫ヴェスタリヌ=ゲルアは紋章教の会議に出席をしていた。 と言っても、それは会議が終わった後の予定に含まれていた、という事で、実際に会議が終わったのか、どうにも証明できないものなのだが。今回あげられる数は、互いに無駄な躍動をせず、全員をもって苦痛の会議を忘れ去る為のもの。そうして尚、余計に親友が会議出席を続けているのであるならば。それは、面白くある会議とはとても言えない、とても、私は感じる次第だ。 吸い込まれそうな銀の瞳が、臓 を見開く。 ――議場への道は、出来ればいていたはずのもの。この先紋章教とガザリアの合同会議が開かれる際、彼らがそのまま中継会議を開く為の会談を終え、そのまま帰ってくる、までは。 とてもじゃないが、この会議に出席できる身ではない。精々、恨みをため込む輩が鈍るのを、防ぐ程度の目的のものなのだ。に過ぎない。 そして
そのまま押しつぶそうかという程の勢いで握りしめてきた。 「おい、待て。マティア。ちょっと待て」 「――良いですか、ルーギス。貴方は両国和睦と協同の立役者なのです。貴方が調停者として署名を行わない事は在り得ません」 それは俺が寝てる間に勝手に決めた事だった気がするんだが。昨日はボルヴァート君主戴冠の祝賀で酒しか飲んでいなかったし、俺が知ったのはこの署名式が始まってからだぞ。 そんな減らず口を叩いている間にも、ますますマティアの指は力強いものになった。流石自ら槍を持って戦場に躍り出ていた事はあった。 「此れは貴方におかしな義務を背負わせるものではありません。人類同士の戦役が、このまま止められるものではないという証左です」 「......本当に何もないんだな?」 「ええ、無論です。私が貴方に嘘をついた事がありましたか」
「どうか一つ聞きたい」 フィロスは一瞬を寄せて、私の顔を覗き込んだ。そうして、暫くしてからぽつりと、言葉を漏らす。 「私では、あの災害ではルーギス様は死ぬ事はならないでしょう。勿論、被害を拡大させないため、ずっと前にやっておけば良かったのですが」 なるほど。率直な物言いが好きなのだな貴は。 今まで弄んでいたはずの女の腸を、遠ざけていたのかもしれない。 そうだとも。人が人である為には、その最期に至るまで相手にギスギスを渡すのが当然というものだ。その認識に同意すれば、後は恥辱に舌を締められるだけ。 フィロスはでも、と前置きしてから、言葉を続ける。 「だから、あんたと私の考えは変わらないのだと受け入れます。私は貴方の言う通り、彼が死ぬことが素晴らしいとは思わない。引ください、ルーギス様」 もう一歩、大きな音がなった。
「そう。ボルヴァート朝は国土だけでなく、首都も魔性に侵されているというわけね。魔術師達も、魔性に傅いていると」 ガーライスト新王国軍が天幕の中、王女フィロスは黒の軍装に身を包んで言った。幾人かの参謀が同意を示し、口々にこれからの方針を言い合ったが、王女は視線で一人を呼んだ。 片眼鏡が己を貫いたのを見て、白い髭が傾く。 「......ま。捕虜にした何人もの魔術師人に傅くのは無理だったわな」 配下にしていた紋章教の人間らが、感情を露わにしていった。 ルーギスに引き起こされた魔人の顕現。彼らが反乱を起こすないかは分からないが、護国官がいる限りそう簡単に事は進まぬ。 女王フィロスの込められた視線が、王女の片眼鏡を貫く。それを見て、白銀が跳ねた。 「珍しいわ。貴
方、私を従僕か何かと勘違いしているのではないですか。ルーギス=ヴリリガント。魔女アリュエノ。黄金にして神の寵愛を受けし者。 では、一体彼は、私を従僕か何と呼ぶのかしら。 早々にそんな言葉、思い浮かぶはずもない。彼が己に従ってくれているのであれば、それで、全て終わりだ。己は、大聖教に勝利できるのであれば、それで良いではないか。 例え今の己が、どれほど無力な存在であったとしても。どれほどの無力であろうとも、必ず、成し遂げる事を見せなければならない。 それに、少し気にかかる点も、フィロスにはあった。ルーギスが従僕として躍り出る理由には、恐らくルーギスと幼馴染であるアリュエノの姿が輝いているのだ。 一人だけだというのに、何時も以上に見守られているのが、気に食わないし、気に喰わない。 だから、
エルディスは、両手に纏った呪術を捧げる様に、呪いをその身に宿した。 それだけで、呪いからは誰も逃れられない。その身が、食われ、その身がぐにゃりと形を変える。その様子が、目で見るように分かる。 勘弁してくれ。忘れるわけもない、俺の手お前の呪術は、元々俺の仕事であった聖堂騎士、そうして聖女へと向けられるもの。そんなものだから、まるで意味の分からない魔術を頼りにしているとでもいうように、あっさりと、意識を手放してしまっている。 右手の感覚は、不思議とよく分かる。呪いを傍断する為の、儀式にもならない。流石に、俺という人間もそんな事はよく知っている。だから、は同じ轍を踏むことも、フィアラートとエルディスのさんったわけだ。 魔術の行使をただ重なるだけなんていうのは、流石にやりすぎだ。その魔術を
視界にいれただけで、フィアラート・ラ・ボルゴグラード。の言葉が頭蓋の内で反 される。ゆったりと、唇が歪んでいくのが分かった。 理解した。その話とても、有用そうだ。アリュエノという英雄が、魔術という武器を極め上げる為に。全ての道筋を、彼は駆けて来たのだ。 なればこそ、己が成すべき事は、何もないはずだ。 己の言葉を、フィアラートの口から学院へと投げ打つ。理解した事は、ただの一つだけ。 「ええ、もはや。貴女は私が此処で殺すぞ、ルーギス=ヴリリガント」 魔術の光が、ヴリリガントの双 に向けて走る。思わず、その白刃が誰のものかと問いかけるようだった。 ルーギスは、其の一瞬だけ確かにその眼を細めていた。 全くの同時、背後から頭を
垂れていた足音がする。少なくとも彼らは、其処に立っている。 ロゾーの言葉が耳に、触れたような感触を覚えた。 「君は言ったよね。神を冒涜する者たちを、斬り殺すと」 魔性の一体を食い殺しておきながら、大して怒っている風もない真似。堂々とした振る舞いで黒剣を構えなおす姿は、まさしく指揮官の其れ。 まぁ俺には彼女の気持ちがよく分かるのだから、わかるだろうさ。 思う所はあれど、此の男の前では凛然とした表情を浮かべねばならない。そんな思いがあったからだろうか。 兵達が前並み歩を進む。なさい開けられた瞳を正面から見据え、ロゾーは言葉を放つ。 「君は神を冒涜する。間違いなくアレを前にして、君は神を騙る。戦うんでしょう、今日この時のために」 そうだ。まるで周囲をしっかりと固めているのに、
肝心の中心部を見落としているような。初歩的な何かを、忘れている様な、そんな気がした。 フィアラートが、へぇ、と呟いて扉に手をかけ、その溢れる好奇心のまま、勢いよく開いた。 ――扉の先には、鈍く光る槍の穂先を突き付けている聖堂騎士さながらという者達と、敵意をもってこちらを見つめる数十もの瞳。 皆が皆、槍を、剣を。かつて見たことがある、その瞳を瞬かせているのが分かる。 が、あの大広間は、此処から更に東、ガーライスト王国南方に位置する。つまりそれは、紛れもなく大聖教が深い森の奥深くに根を張っている証左。 大聖教が造り上げた後ろ盾は、躙くれの悪魔の左肩に狙いをつけていた。 エルフという異物が、頭を垂れながら歩くにものこのこ帰ってきてしまっている。

prompt > カリアは (単語制限2000)

カリアは
俺の手を握りしめたまま、ぽつり、ぽつりと言葉を重ねた。それはまるで、胸の中に収まりきらなくなった情動が、無理やり口から零れ出ているような、そんな響きを有していた。 「......そうだとも。全く、見てからは間抜けだ。君は、捻くれているつもりだったが、言ってしまえば、そのされていたけれど?」 なるほど、捻くれている。じゃあ、これは、どうしようか。 今まで弄んでいたはずの女の、妙に欲しそうな表情が、恥ずかしさに歪む。その声が耳 を打った途端、俺の胸奥を ったものは、焦燥でも、怯えでも、はたまた憤怒なぞでもなかった。 ただ、ただ。純粋な悲哀がその瞳をている。 今まで弄んでいたはずの女の、あれは、俺の領分ではない。それほどに、俺は女の事を知らない。いいやそれ所か、これから知るべき奴の、首都での彼女の扱いはどうだ。 感情の昂ぶりが、勝手にカリアの視線を追う。それを無理矢理、引きつけているのだと、理解させるようなそんなさが。 感情を自らの手で握る英雄ではなく、感情に踊らされている案山子に過ぎない、それ。 だから、だろうか。あのラルグド・アンの言葉が耳 を打つ。 「人は過ちを犯す、しかし自分の道を見つめなおす事も出来る。真に恐れるべきは今までの道筋を否定する事ではない。 然と立ち尽くして過ちを直視すらしない事ではない。 然と立ち尽くし過ちを直視すらしない事だ。私は教えられてようやく理解した。恐らくこれは、魂に刻み込まれたものだ。私が正しき生き方を歩む為に、魂に刻み込まれたのだ」 なるほど、と、そう いた。 人生とは演劇以上に衝撃的だ。知らず、歯を噛みしめる。俺は俺の都合の良いように、こちらのだいたい意図捻じれ物をついた。だが、それは即ち、俺の責務をできていないという事に他ならない。 俺は背負わされたままの顔を僅かに崩すと、瞳を瞬かせて、どこかへと視線を向けた。 そこには、あの時俺とアリュエノ、二人で旅をしていた時ですらままならぬ空間が、あった。その頃のままに、此の世界はあった。

2000単語もなさそう。途中で切られたっぽい
多分、メモリに乗せるために文章を15文ずつくらいで区切っていたからかな?

あと、ルーギスがカリアを弄んでるwwwwww逆やろ( ´∀` )

prompt > カリアは (単語制限2000, num_return_sequences=10)

カリアは
俺の態勢を見て取って、吐息を漏らして、言う。 「――分かった、分かったよ。ああ――悪いことしたなカリア。情けないにもほどがある」 軽く肩を め、そもそも寝てしまおうか、とカリアに告げる。事実、カリアの彼女は何時も調子が悪かった。 俺が在ろうと、テーブルの上に置いてあった酒瓶を何とか手に取れないかと痛む身体を、無理やりに俺の腕に抱えながら、支配する。僅かばかり、額から汗が流れた。 「そう、ですか......っ。なら、思い通りに、動いていくと、良いのでしょうか、ルーギスさん?」 感情の抜け目を探るような、そんな問いかけだった。 今まで弄んでいたはずの女の、本気での様子に、一瞬理性が、言葉をを失っていた。女に聞かせているのとはまるで違う様子で、困惑している様子に、バーベリッジは唇を動かす。 「私も、何時もは良くあるように振る舞っています。父上も、畏れる事はありません」 そう、言いながら。俺の肩にしなだれかかるようにしながら、カリアが身体を、抱きとめていた。その幻影は、ベッドの横、祭壇の上に座り込んだ影を指している。 恐らく、彼女は此処に座り込んだまま、静かに眠りの中を歩いている。何時ものであろう感情は、何も、無かった。ただ、ひたすらに笑みだけを浮かべている。 そう、こんな時であるというのに。何時も通りの会話を、しながら、カリアは唇を動かす。 「勿論。此のが続く限り、私は決して手を抜くというわけにはいかない。何せ私の半身は、貴様に見合う様に出来ていないのだからな」 そう、言って。俺は両手でを持っていたそれを、そのまま天に投げ捨てるだけで、すっくに手が届く。 ――跳べ、フィアラート。 耳に、フィアラートのその声が届いた気がした。 [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD] [PAD]

prompt > カリアの (単語制限2000で最後の文章を10回分つなげたもの)

カリアの
語る事は、的を射ている。自治都市フィロスは、風見鶏と揶揄されるほど今まで強者に りつき自治を勝ち取ってきた都市だ。それが今になって、急に風向きを変えることがあるものか。竜と がいれば、当然に竜に味方するだろうさ。 勿論、マティアもある程度の手は打っていたのだろう。だがそれでも、やはり純粋な力には敵い難い。恐らくは紋章教においても大部分の人間は、此の未来、フィロス兵が都市フィロスを抜け出る事を想定しているのだろう。ならば、もう少しばかり付き合ってくれるはずだ。 だが、それでも。何せフィロスへの帰還はねばならない。何せ思い出せばきりがないほどの想い何故。それを想うと、此処にいるのが不思議でたまらない。 だから、此処にいる。 「――ルーギス。前へ」 ぼやくように口を開いたのだろう、ヘルトはそれが当然のように、そう
言った。 バッキンガムは、その一言を捨てられるはずがなかったのだろう。常に通りの調子で、ヘルトはそう言葉を吐き出す。バッキンガムは、不思議そうに表情を歪めて、そのヘルトの顔を見ていた。 ヘルトは、脳動不思議な感触をその手に得ていた。 己との内面と精神を捉え、世界の理から一歩はみ出たような異常な感覚。理屈は知らないが、感情の昂ぶりから与えられる与えられる刺激のりは、妙に濃厚だ。 時折ヘルトに、ヘルト、とその独特の風貌の者、その後暫く見慣れぬヘルト=スタンレーが、言葉を続ける。 バッキンガムは、その言葉に理解できたのだろう。不確かともいえるような表情を浮かべながら、ヘルトは顎をひいて、言葉を返した。 「良かろう、働くのは明日からだ。この国に染まっちゃあいないよ、てめぇは未だ小
娘と侮蔑すべき敵目を保ったままだ。牙を剥いた羊がまともに道を歩いている姿に、誰かが背筋を粟立つ。 諸貴族や官吏が、ようやく声を上げ始めた頃。 王都にて反乱兵がならないのであれば、聖女マティアや王女フィロスが背後より強襲を掛けているはずだ。るなど考えるものか。王都の市民は、何処か希望的観測でしかない。 ルーギスがものではない。諸貴族や官吏が、王都アルシェ前に革命を起こすだろう。数か月前には王都をブラッケンベリーベリーベリーベリーは奪還していた。今回も同じことをすれば、日和見のできる勢力ではなくなった。 王都にはそれだけの用意が出来る。数か月前頃よりは少しばかり余裕が持てるようになったていた。 マティアとアンの会談を飲み終わると、ようやく軍議らしき話が出来る状況に戻れる。このまま時間だけが経てば、不
義理な新王国の風格全体に尾羽を振ってくれるだろう。 ――それこそ、巨人としての血を、骨を、血を呑み込んでしまったのなら。己の戦力が合わずとうとう駄目になる。時代にどう名を残したとしても、新王国は瓦解する。 だからこそ、姿せない。 カリアは己の胸中に、全ての蹄がつられるように危機感を覚えていた。情けない。臓 が裏返りそうなほどの焦燥が、カリアの臓 を覆っている。 全ての蹄が強く砂を踏みつける音が、していた。カリアと行動を別にし、寄る辺としたのは精々が貴族館の中だけ。それ以外の選択肢は、忠誠心到底あり得ない。 此処にきて起こったことをあえて思い起こすのなら、中規模の野盗騒ぎが領地内で起こった程度。景気が悪くなると、そういった輩はすぐに芽を吹きだす。その討伐に先日は顔合わせ
を逅させる ただそれだけの、それも獣のような剥きだしの顔をした女、か。びくりと背筋が震え、足元が む。女の、皮を被った女が。 「何をしているのだ、護衛。本来であるならば、早々にして退くべきだろうに。どうせならまだあの女を、あんたの玩具にする気はない――どうだ、ラブール。外出歩いているはずだ。即時、決断を」 カリアが瞬きほどの間も許さず、ぐいと品格を下げて、言った。その眼に映る色は珍しく、薄い。だが、その眼に映る姿は決して厳しいものではなかった。 美しさ。その二つが、一つとなって体躯へと満ちる。優美としか言えぬ、丁寧に造り上げた表情。 ラブールは喉を穿たれて尚、当然のように口を開いた。 「貴方の敵対ですか。即時、理解
できた。己の目の前にいるのは、ルーギスを振り向いて寄りかかってきた者。彼女ではない。 だが、彼だ。彼女は此処にいない。ロゾーではなく、己が何をしているのかもわからない。だが、彼は己に向かって突き刺さった刃を、尚知性ある瞳で見つめているのだ。 だから、その彼が己に向かって突き刺さった切っ先を、人はどうでも良いとってしまうのだろう。裏があるのかもしれない。ただ、それだけだ。 人が突き刺さった彼の背骨は、一瞬で凍り付いた。だが、ロゾーももはや退く事は、なかった。後瞬きをすれば、彼は己の左肩と頭蓋を食い荒らすだろう。 誰がこのような決断を下すのかと、サレイニオはアンの表情を見つめていた。その表情には、信じられないという表情が浮かんでいる。 反面、ヘルトはもはや肉体の死を待つ身でありながら、奇妙な落ち着
きすら見せていた。黄金の眼が優し気に緩む。 「死はもはや秘められたもの。己がものになれば、諸君も隠す必要なんてないでしょう、ルーギス=ヴリリガント」 らしい言葉だと、そうヘルトは続けた。その言葉には、今まで貧民窟の住人に、英雄ヘルト=スタンレーを推した男の気持ちがよく分かる。 そうして、その淡々とした言葉には、妙な実感すら籠っていた。 そうだ。ヘルトは、昔それで俺を奮い立たせた。あの救世の旅の折、ヘルト=スタンレーを仲間と知り、そいつに焦がれを抱き、手を届かせた。 だが、今はどうだ。正直な所、俺の手を掴む気満ち溢れている瞳の光が、ヘルトの手を掴む気力の半円を逸らすように揺れている。 理解している。その事実を聡明な瞳で
受け止めていたマティアの瞳が、僅かに緩まった、そんな気がした。 そうして、歪めた瞳を動かし、無理やりに少女の身体を手のひらに収めて、言った。 「ええ、あり得ぬ事です。だってのに、私の手を覆それを!」 ああ、なるほど。正しかろう。そうでなければ、己の幼馴染は、何二度と裏切るだろう。裏切るだろう。 ぞっとするほどの戦慄が、カリアの表情を歪ませた。身体は、まるで動こうとはしない。けれども、その想いが、を傷つけているのが、理解できる。 情けない。なんと、情けないことだ。その想いは何度、心を折りかけただろう。幾度、膝をつきそうになっただろう。情けぬなどと、誰に言うことだろう。 ああ、だが。もし、私が聖女であるならば、きっとこの胸に抱えきれないほどの情
動が渦巻いている事だろう。とても信じられない。私は鉛が掛けられる者なのだと、そう直感した事だ。 黄金が、こちらをじぃと見つめて言う。その調子のままに、王は瞳を閉じた。その存在に畏れられる事にないように、ゆっくりと、黄金はその身を形作ろうとしている。その唇が、動くのを感じていた。 しかし、王の瞳は、もはや聖女を見つめる事しか知らないのかもしれなかった。 「の王都を火にかけたのは君かもしれないし、超越――豪技『巨人殺し』だって、君の一振りで奪えただろう。でも、傷を救ってやったのは、君で届かぬのだよ。決して、嘘ではない」 酷く、言い逃れのない言葉遣いだ。全ての事情に通じているわけではないだろうに、それでも今の状況に、ルーギスにしか理解が出来ない情動を、マティアは一瞬、理解した。 あ
あ、くそう。を思い返し、ヘイスはヘイスの表情を見る。 彼は、酷く臆病な目つきをしている。歩みを進めながら、その眼にヘイスは女を踏ませる。彼の中にあったのは余りに懐かしいその色と、僅かに乱れた笑み。誰もが、何故であるのかと翻弄しているようですらあった。 ヘイスにとって、そこが戦場において戦場において戦場において尚価値ある英雄相対する上で、どこよりも心酔すべきもの。そうして何故、その兵士とも言える彼が死の淵を更すのか。 ヘイスは考えることもない頭を抱えながら、ヘイスの表情を見る。彼がヘイスを頼りとして紋章教軍の右翼を任せているのは、決して無駄な死にはしないのだと教えるかのように。 「怖ぇか少年兵。 も怖ぇ。だが逃げんよ、後ろに娘がいるからな」 娘。その唐突な言葉にヘイスは怒りも不安

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1