青空文庫のテキストから作成したコーパスを Hugging Face で公開しました

# 前書き

ChatGPT が広く一般に普及するようになり、一年が経とうとしています。
国内でも様々なプレイヤーが独自に大規模言語モデル (LLM) を構築しようとする動きもあり、綺麗な日本語データの重要度が高まってきました。

綺麗な日本語という観点では、多少表現に古めかしさはあるものの、[青空文庫](https://www.aozora.gr.jp/)に掲載されている作品群は申し分ありません。
しかしながら、青空文庫のテキストには[注記](https://www.aozora.gr.jp/annotation/)が施されており、 LLM をはじめとした言語モデルの学習にそのまま使おうとするとノイズになってしまうという課題があります。

[グロービス経営大学院](https://mba.globis.ac.jp/)の学内シンクタンク「[グロービスAI経営教育研究所](https://mba.globis.ac.jp/feature/technovate/gaimeri/)」では、国内最大のビジネススクールのノウハウと、企業の人材育成や組織変革など教育現場から得られる知見と、AI（人工知能）をはじめとするデジタルテクノロジーや認知科学の発展がもたらすイノベーションを統合した次世代経営教育モデルを研究開発しています。
研究開発の一環として構築した、注記を処理した青空文庫のコーパスを Hugging Face で公開いたします。

# 概要
| データセット名 | 概要 |
|:-|:-|
| [globis-university/aozorabunko-clean](https://huggingface.co/datasets/globis-university/aozorabunko-clean) | 注記を処理した青空文庫テキスト |
| [globis-university/aozorabunko-chats](https://huggingface.co/datasets/globis-university/aozorabunko-chats) | 処理済みテキストから簡単なヒューリスティックスで会話のような部分を抜き出したもの |

## [globis-university/aozorabunko-clean](https://huggingface.co/datasets/globis-university/aozorabunko-clean)

青空文庫に含まれる注記を処理し、生のテキストのみを取り出したものです。

[手法](#手法)で詳しく述べますが、大まかな処理としては以下の通りです。

- 本文が重複するデータの除外
- ヘッダおよびフッタの削除
- ルビの削除
- 外字の Unicode 文字への変換
- 内容に影響しない注記の削除

### 利用例

```py
>>> from datasets import load_dataset
>>> ds = load_dataset('globis-university/aozorabunko-clean')
>>> ds
DatasetDict({
    train: Dataset({
        features: ['text', 'footnote', 'meta'],
        num_rows: 16951
    })
})
>>> ds = ds.filter(lambda row: row['meta']['文字遣い種別'] == '新字新仮名')  # 新字新仮名に限定
>>> ds
DatasetDict({
    train: Dataset({
        features: ['text', 'footnote', 'meta'],
        num_rows: 10246
    })
})
>>> book = ds['train'][0]  # 作品ひとつ
>>> book['meta']['作品名']
'ウェストミンスター寺院'
>>> text = book['text']  # 本文
>>> len(text)
10639
>>> print(text[:100])
深いおどろきにうたれて、
名高いウェストミンスターに
真鍮や石の記念碑となって
すべての王侯貴族が集まっているのをみれば、
今はさげすみも、ほこりも、見栄もない。
善にかえった貴人の姿、
華美と俗世の
```

## [globis-university/aozorabunko-chats](https://huggingface.co/datasets/globis-university/aozorabunko-chats)

こちらも[手法](#手法)で詳しく述べますが、前出 [globis-university/aozorabunko-clean](https://huggingface.co/datasets/globis-university/aozorabunko-clean) のテキストデータから `「...」` だけからなる行が連続するものを抽出するという簡単なヒューリスティクスで作成した対話データセットです。

実際には独白なども含まれるため適合率 (precision) はあまり高くなく、また会話の表現は一般的には `「」` で囲まれた行の連続以外にも様々なので、再現率 (recall) は低いです。

従って、例えば LLM のインストラクションデータとしてそのまま使うのは難しいと考えられますが、何かしら利用価値があることを期待して公開します。

### 利用例

```py
>>> from datasets import load_dataset
>>> ds = load_dataset('globis-university/aozorabunko-chats')
>>> ds
DatasetDict({
    train: Dataset({
        features: ['chats', 'footnote', 'meta'],
        num_rows: 5531
    })
})
>>> ds = ds.filter(lambda row: row['meta']['文字遣い種別'] == '新字新仮名')  # 新字新仮名に限定
>>> ds
DatasetDict({
    train: Dataset({
        features: ['chats', 'footnote', 'meta'],
        num_rows: 4139
    })
})
>>> book = ds['train'][0]  # 作品ひとつ
>>> book['meta']['作品名']
'スリーピー・ホローの伝説'
>>> chats = book['chats']  # 会話のリストで、 list[list[str]] 型
>>> len(chats)
1
>>> chat = chats[0]  # 一連の会話で、 list[str] 型
>>> for utterance in chat:
...     print(utterance)
...
人生においては、たとえどんな場合でも必ず利点や愉快なことがあるはずです。もっともそれは、わたくしどもが冗談をすなおに受けとればのことですが
そこで、悪魔の騎士と競走することになった人は、とかくめちゃくちゃに走るのも当然です
したがって、田舎の学校の先生がオランダ人の世継ぎ娘に結婚を拒まれるということは、彼にとっては、世の中で栄進出世にいたるたしかな一歩だということになります
```

# 注意事項

## 文字遣い種別について

作品は、文字遣い種別 (新字新仮名、新字旧仮名など) に関わらず今回のコーパスに収録されています。

なるべく現代の言葉に近づけたい場合は「新字新仮名」に限定することになるでしょう。
以下のような条件でフィルタリングをすることができます。

```javascript
row["meta"]["文字遣い種別"] == "新字新仮名"
```

なお、当然ながら、「新字新仮名」に限定しても昭和や大正、明治の趣を感じる文章であることは言うまでもありません。

また、青空文庫では文字遣い種別のみが異なる同一の作品も収録されています。
この重複が問題になる場合も、やはりフィルタリングを検討する必要があります。

## メタデータ (特に筆者) について

後述の「作品リスト」の構造と本文重複削除の都合上、筆者が複数いる場合や、翻訳者がいる場合に、一つを残して全てのメタデータが破棄されています。
このため特に、残ったメタデータ以外に記載されている筆者や翻訳者はコーパス中にメタデータとしては登場しません。

それらのメタデータが必要な場合は、「作品リスト」を併せて参照する必要があります。

## ルビについて

作品の表現としてルビ無しでは到底読めないようなものが振られる事がありますが、一律で削除しています。

# 手法

手法は以下の通りです。

これを再現するコードは [globis-org/aozorabunko-extractor](https://github.com/globis-org/aozorabunko-extractor) で公開しています。

## 1. データ収集

まず、 [公開中　作家リスト：全て](https://www.aozora.gr.jp/index_pages/person_all.html)ページで「公開中　作家別作品一覧拡充版：全て(CSV形式、UTF-8、zip圧縮）」として提供されている CSV ファイルをダウンロードします (以下、このデータを「作品リスト」とします)。
この作品リストに含まれる情報は、この度公開したデータセットの `meta` フィールドの値としても使われています。

青空文庫にはパブリックドメイン化していない、著作権の残ったデータも含まれています。
個別に精査すれば Creative Commons ライセンスの下で利用可能なものもありますが、確認は手間なので、ここでは一律で取り除きます。

作品リスト中の各作品の本文テキストを取得し、 UTF-8 で `text` フィールドとして加えます。

## 2. 重複削除

作者が複数いる場合など、作品リストには同一の作品を指す行が存在します。

まず、「作品ID」と「人物ID」が「図書カードURL」と一致していない行を削除します。
(「図書カードURL」は `https://www.aozora.gr.jp/cards/{人物ID}/card{作品ID}.html` のように構成されていますが、「人物ID」は筆頭著者のものになるようです。)

何らかの理由で、これだけでは全く同じ本文のデータが残るので、先勝ちで作品リストの後に出現した方の行を削除します。

前述の通り、この作業の過程で、作品リストに含まれるメタデータが最終的に出来上がるコーパスから一部欠けることになります。

## 3. クリーニング

以下の順番で注記などを処理していきます。

a. 改行を LF (`\n`) に変換する
b. ヘッダーを削除する
c. フッターを削除し、 `footnote` フィールドに加える
d. 割り注を通常の括弧書きにする
e. ルビを削除する
f. 外字や踊り字 (繰り返し記号) を Unicode 文字に変換する
g. 残った注記を全て削除する
h. 本文の前後に残った改行と罫線を削除する

### a. 改行を LF (`\n`) に変換する

青空文庫は CRLF (`\r\n`) ですが、 Hugging Face では LF が多い (気がする) ので LF を採用しています。

### b. ヘッダーを削除する

基本的に、ヘッダーは以下のような構造をしているので、正規表現で取り除きます。

```
{作品名}
{著者名}

-------------------------------------------------------
【テキスト中に現れる記号について】

／＼：二倍の踊り字（「く」を縦に長くしたような形の繰り返し記号）
（例）わざ／＼
...
-------------------------------------------------------
```

例外的なパターンがありますが、個別に対応します。

例えば、 `【テキスト中に現れる記号について】` で始まるヘッダが多くの作品に付与されていますが、これが `【テキス禊中に現れる記号について】` という誤字で出現したり、  `《テキスト中に現れる記号について》` のように `【】` ではなくて `《》` で出現したりします。

目次がある場合も、取り除きます。

### c. フッターを削除し、 `footnote` フィールドに加える

本文末尾の `底本：` 以降を削除します。
(`［＃本文終わり］` と言う注記も定義されていますが、青空文庫にはおそらく一度も出てきません。)

ここも例外的なパターンがあるので、個別に対応します。

削除した情報は `footnote` フィールドに加えています。

### d. 割り注を通常の括弧書きにする

割り注とは、行の中に二行で(？)小さく挿入される注記です。

> 伝説の時代　［＃割り注］タマス、ブルフインチ著［＃改行］野上彌生子訳［＃割り注終わり］　定価弐円　尚文堂発行

を

> 伝説の時代　(タマス、ブルフインチ著 野上彌生子訳)　定価弐円　尚文堂発行

のように変換します。

### e. ルビを削除する

ルビは `《》` を用いて記載されるので、これを全て削除します。

一部、 `｜` を用いてルビの範囲指定を行うことがあるので、これも削除します。

> 霧の｜ロンドン警視庁《スコットランドヤード》

↓

> 霧のロンドン警視庁

前述の通り、作品の表現としてルビ無しでは到底読めないようなものが振られる事がありますが、一律で削除しています。

### f. 外字や踊り字 (繰り返し記号) を Unicode 文字に変換する

外字は、注記から Unicode で復元可能なものについて復元しています。

> ※［＃「金＋肅」、第3水準1-93-39］

のように記載されている場合は `1-93-39` の部分を `code` として、以下のようなコードで Unicode 文字に変換できます。

```ruby:Ruby
m, k, t = code.split('-').map(&:to_i)
euc_code_number = ((m - 1) * 0x8f) << 16 | (k + 0xa0) << 8 | (t + 0xa0)
euc_code_number.chr(Encoding::EUC_JIS_2004).encode(Encoding::UTF_8)
```

但し、 Unicode で一文字では表現できず、合字で表現する必要がある場合があります。
これは辞書的に解決します。

```ruby
  '1-5-87' => 'カ゚',
  '1-5-88' => 'キ゚',
  '1-5-89' => 'ク゚',
  '1-5-90' => 'ケ゚',
  '1-5-91' => 'コ゚',
  '1-5-92' => 'セ゚',
  '1-5-93' => 'ツ゚',
  '1-5-94' => 'ト゚',
  '1-6-88' => 'ㇷ゚',
  '1-11-45' => 'ə́'
```

> ※［＃「口＋奧」、U+5662、77-下-14］

のような表記の場合、 `0x5662` から Unicode 文字を得ます。

それ以外の場合、注記の記載を以下の例のようにフォーマットを改めて採用しています。

> 劉之※［＃「二点しんにょう＋隣のつくり」、105-8］

↓

> 劉之※（二点しんにょう＋隣のつくり）

踊り字は、青空文庫では `／＼` や `／″＼` という注記で表現されています。
Unicode に踊り字は存在しますが、少なくとも私が使っているフォントでは横書きに対応していません。
そのまま残す手もありましたが、 Unicode の踊り字の方がセマンティクス的に正しいという判断をして、 変換しています。
それぞれ、 `〳〵` と `〴〵` に変換されます。

### g. 残った注記を全て削除する

注記は　`［＃...］` と言うフォーマットで行われるので、これを削除します。
注記はネストしている場合があるので、注意します。

### h. 本文の前後に残った改行と罫線を削除する

本文の前後の改行と罫線を処理します。

罫線は、 `=` または `-` がある程度連続する部分とします。

## 番外. 対話抽出

[globis-university/aozorabunko-clean](https://huggingface.co/datasets/globis-university/aozorabunko-clean) のテキストデータから `「` と `」` で囲まれた表現のみからなる行を*発話*として取り出し、*発話*行が 2 つ以上連続するものを会話と推定し、抽出しました。

例えば、芥川多加志著「四人」の一節、

> 「憂鬱さうだね。」と坂谷。
> 「うん。」
> 「元気がないね。」
> 「うん。」
> 「いつもそんなに黙つてゐるのか。」
> 「うん。」
> 「何とか云へよ。」
> 俊一は突然奇妙な調子で云ひ出した。

の場合、最初の行は `と坂谷。` が末尾にあるため含めず、

- うん。
- 元気がないね。
- うん。
- いつもそんなに黙つてゐるのか。
- うん。
- 何とか云へよ。

を会話として抽出します。

[概要](#globis-universityaozorabunko-chats)で言及した通り、実際には独白なども含まれるため適合率 (precision) はあまり高くなく、また、例の最初の行のように、会話文が文中に埋め込まれていることも多いので、再現率 (recall) は低いです。

# ライセンス

作品そのものは PD ですが、 作品リスト (`meta` フィールドの元データ) が CC BY 4.0 ライセンスで公開されているため、当データセットも同様に CC BY 4.0 とします。