latent state // ログ
2026年10月27日 // NLP · ML // 12分

LLM [2] データの準備

テキストをクリーニングする:ヘッダー、言語、空行、<|endoftext|> による連結。

前回の記事でダウンロードしたアーカイブを開く。モデルの学習に使う本はこのフォルダにある:gutenberg/data/raw/PG*_raw.txt

  • 51,667 ファイル
  • 20 GB
  • メタデータ:metadata/metadata.csv

この記事のコード:https://github.com/format37/gpt

プロジェクトのルートに config.yaml を置く。自分のパスを書き込んでほしい。私の場合はこうだ:

prepare_dataset:
  metadata_folder: /home/alex/projects/gutenberg/metadata/metadata.csv
  data_dir: /home/alex/projects/gutenberg/data/raw
  target_dir: /home/alex/projects/gutenberg/data/target

任意の PG*_raw.txt ファイルを開いてみる。

The Project Gutenberg EBook of Journal des Goncourt (Deuxième série,
deuxième volume), by Edmond de Goncourt

This eBook is for the use of anyone anywhere at no cost and with
almost no restrictions whatsoever.  You may copy it, give it away or
re-use it under the terms of the Project Gutenberg License included
with this eBook or online at www.gutenberg.org


Title: Journal des Goncourt (Deuxième série, deuxième volume)
       Mémoires de la vie littéraire

Author: Edmond de Goncourt

Release Date: December 30, 2005 [EBook #17420]

Language: French

Character set encoding: UTF-8

*** START OF THIS PROJECT GUTENBERG EBOOK JOURNAL DES GONCOURT ***




Produced by Carlo Traverso, Mireille Harmelin and the
Online Distributed Proofreading Team of Europe. This file
was produced from images generously made available by the
Bibliothèque nationale de France (BnF/Gallica) at
http://gallica.bnf.fr.





JOURNAL DES GONCOURT
_Mémoires de la Vie Littéraire_


DEUXIÈME SÉRIE--DEUXIÈME VOLUME--TOME CINQUIÈME
1872-1877


PARIS, BIBLIOTHÈQUE-CHARPENTIER, 11, RUE DE GRENELLE.
1891




PRÉFACE

(_Réponse à Monsieur Renan_)


Monsieur Renan me faisait l'honneur de me dire, il y a des années, qu'une
lettre fausse avait été publiée par LE FIGARO, comme émanant de lui, et
que son dédain de l'imprimé était tel, qu'il n'avait pas réclamé.

Le monsieur Renan de l'année dernière, est vraiment bien changé.

A propos de vieilles conversations de 1870, rapportées dans mon Journal:
voici la lettre, que le _Petit Lannionnais_ publiait de l'auteur de la VIE
DE JÉSUS-CHRIST.

      Paris, 26 novembre, 1890.

      Ah! mon cher cousin, que je vous sais gré de vous indigner pour moi,
      en ce temps de mensonge...

1 ファイルが 1 冊の本だ。ヘッダーにはライセンスが入っている。その後にタイトルと、空行と 3 つのアスタリスクで区切られた段落が続く。テキストはチャットで見えるのと同じ形にしたい:空行で区切られた段落だ。

使うファイルを選ぶ:

  • 英語のテキスト
  • 実際に存在するファイル
from tqdm import tqdm
import os
import yaml
import pandas as pd
from cleanup import strip_headers

def file_list(config):
    target_dir = config["target_dir"]
    if not os.path.exists(target_dir):
        os.makedirs(target_dir)

    # Select only english texts
    df = pd.read_csv(config["metadata_folder"])
    print('Initial size:', len(df))
    df = df[(df.language=="['en']") & (df.type=='Text')]
    print('English texts:', len(df))

    # Only existing files
    files = os.listdir(config["data_dir"])
    downloaded = [file.split("_")[0] for file in files]
    df = df[df.id.isin(downloaded)]
    print('Existing:', len(df))

    # Compose file path
    pathes = []
    for name in df.id:
        path = os.path.join(config['data_dir'], f"{name}_raw.txt")
        pathes.append(path)
    return pathes

ライセンスを取り除くには cleanup.py が必要だ。data/ フォルダにあることを確認してほしい。元は c-w/gutenberg のもので、短い本でも末尾が削られるように、*** END OF マーカーの探索にあった 100 行の制限を外した。

  • 連続する空行を 1 つの空行にまとめる
def collapse_blank_lines(text):
    result = []
    for line in text.split("\n"):
        if line.strip():
            # Has text in a line
            result.append(line)
        elif result and result[-1] != "":
            # First empty string after the text
            result.append("")
        # Skip other empty strings
    return "\n".join(result)
  • 本を <|endoftext|> トークンでつなぐ。こうするとモデルは、文書が終わり、次のテキストはそれと無関係だとわかる
  • 最大 500 MB ずつのファイルに書き出す。こうすれば分けて学習できる
def combine_files(files, config):
    max_size_mb = 500
    separator = "<|endoftext|>"
    target_dir = config['target_dir']

    if not os.path.exists(target_dir):
        os.makedirs(target_dir)

    current_content = []
    current_size = 0
    file_counter = 1

    for file_path in tqdm(files):
        try:
            with open(file_path, "r", encoding="utf-8") as file:
                content = file.read()
        except UnicodeDecodeError:
            # Not UTF-8 (UTF-16, cp1252): skip the book
            tqdm.write(f"Skipping {file_path}: not UTF-8")
            continue

        # Remove extra \ufeff garbage
        content = content.replace("\ufeff", "")
        # Remove license
        content = strip_headers(content)
        # Collapse blank lines
        content = collapse_blank_lines(content)

        # Write each 500 mb
        estimated_size = len(content.encode("utf-8"))
        if current_size + estimated_size > max_size_mb * 1024 * 1024:
            target_file_path = os.path.join(
                target_dir, 
                f"combined_{file_counter}.txt"
                )
            with open(target_file_path, "w", encoding="utf-8") as target_file:
                target_file.write(separator.join(current_content))
            file_counter += 1
            current_content = [content]
            current_size = estimated_size
        else:
            current_content.append(content)
            current_size += estimated_size

    # Write tail file
    if current_content:
        target_file_path = os.path.join(target_dir, f"combined_{file_counter}.txt")
        with open(target_file_path, "w", encoding="utf-8") as target_file:
            target_file.write(separator.join(current_content))

    print(f"{file_counter} file(s) saved in {target_dir}")

def main():
    with open("config.yaml") as f:
        config = yaml.safe_load(f)["prepare_dataset"]
    files = file_list(config)
    combine_files(files, config)

if __name__=='__main__':
    main()

実行する:

python data/prepare_dataset.py

私のマシンでは約 23 分かかった。並列化と重複除去はあえてしていない。GPT-2 small の最終的な品質にあまり影響しないところで、複雑にしたくなかったからだ。

重複除去はほぼ同じ本を取り除く。たとえば同じ本が別の版で出版され、両方の版がデータセットに入っていることがある。厳密には文字単位で違うが、テキストはほぼ同じだ。タイトル + 著者の一致で数えると、データセットの重複は約 0.5% で、シェイクスピアの戯曲のいくつかをモデルは 2 回ではなく 4 回見ることになる。

実行中に、次のようなメッセージが 66 件ほど表示される。

Skipping /home/alex/projects/gutenberg/data/raw/PG10511_raw.txt: not UTF-8

これは正常だ。エンコーディングの問題を避けるため、数冊の本を捨てている。

結果を見てみよう:

The Magna Carta

Contents

 The Text of Magna Carta
 Magna Carta 1215
 The text of THE MAGNA CARTA

A note from Michael Hart, preparer of the 0.1 version.

This file contains a number of versions of the Magna Carta, some of
which were a little mangled in transit. I am sure our volunteers will
find and correct errors I didn’t catch, and that version 0.2 - 1.0 will
have significant improvements, as well as at least one more version in
Latin.

Version 1.0 may contain a dozen different versions.

The Text of Magna Carta

JOHN, by the grace of God King of England, Lord of Ireland, Duke of
Normandy and Aquitaine, and Count of Anjou, to his archbishops,
bishops, abbots, earls, barons, justices, foresters, sheriffs,
stewards, servants, and to all his officials and loyal subjects,
Greeting.

KNOW THAT BEFORE GOD, for the health of our soul and those of our
ancestors and heirs, to the honour of God, the exaltation of the holy
Church, and the better ordering...
...on the fifteenth day of June,
in the seventeenth year of our reign.
<|endoftext|>                       SENECA

                  APOCOLOCYNTOSIS

          WITH AN ENGLISH TRANSLATION BY

            W.H.D. ROUSE, M.A. LITT. D.

                       MCMXX

INTRODUCTION

This piece is ascribed to Seneca by ancient

特別なトークン <|endoftext|> が本を区切っていることに注目してほしい。

まとめ:

  • 39,250 冊
  • 最大 500 MB のファイル 30 個
  • 合計 14.3 GB

次のパート、トークナイザーへ進む。