🍣

llm load_datasetからdata_collatorまで

2024/01/15に公開

概要

llmを学習する際にデータをロードしてmodelにデータを渡すまでの流れをまとめました。
今までなんとなくの雰囲気でやっていたので、整理するためのメモです。

load_dataset

Hugging Face Hubまたはローカルファイルから学習データをロードする。
https://huggingface.co/docs/datasets/v2.16.1/en/package_reference/loading_methods#datasets.load_dataset

実行例

from datasets import load_dataset

Hugging Face Hubからロード。
DatasetDictが返る。

dataset_dict = load_dataset('kunishou/databricks-dolly-15k-ja')
print(dataset_dict)
print(dataset_dict['train'])
DatasetDict({
    train: Dataset({
        features: ['index', 'category', 'instruction', 'input', 'output'],
        num_rows: 15015
    })
})
Dataset({
    features: ['index', 'category', 'instruction', 'input', 'output'],
    num_rows: 15015
})

splitを指定した場合、Datasetが返る。

dataset = load_dataset('kunishou/databricks-dolly-15k-ja', split='train')
print(dataset)
Dataset({
    features: ['index', 'category', 'instruction', 'input', 'output'],
    num_rows: 15015
})

データ抽出。

for data in dataset.select(range(5)):
  print(data)
{'index': '0', 'category': 'closed_qa', 'instruction': 'ヴァージン・オーストラリア航空はいつから運航を開始したのですか?', 'input': 'ヴァージン・オーストラリア航空(Virgin Australia Airlines Pty Ltd)はオーストラリアを拠点とするヴァージン・ブランドを冠する最大の船団規模を持つ航空会社です。2000年8月31日に、ヴァージン・ブルー空港として、2機の航空機、1つの空路を運行してサービスを開始しました。2001年9月のアンセット・オーストラリア空港の崩壊後、オーストラリアの国内市場で急速に地位を確立しました。その後はブリスベン、メルボルン、シドニーをハブとして、オーストラリア国内の32都市に直接乗り入れるまでに成長しました。', 'output': 'ヴァージン・オーストラリア航空は、2000年8月31日にヴァージン・ブルー航空として、2機の航空機で単一路線の運航を開始しました。'}
{'index': '1', 'category': 'classification', 'instruction': '魚の種類はどっち?イコクエイラクブカとロープ', 'input': '', 'output': 'イコクエイラクブカ'}
{'index': '2', 'category': 'open_qa', 'instruction': 'ラクダはなぜ水なしで長く生きられるのか?', 'input': '', 'output': 'ラクダは、長時間にわたってエネルギーと水分で満たされた状態を保つために、腰の脂肪を利用しています。'}
{'index': '3', 'category': 'open_qa', 'instruction': 'アリスの両親には3人の娘がいる:エイミー、ジェシー、そして三女の名前は?', 'input': '', 'output': '三女の名前はアリス'}
{'index': '4', 'category': 'closed_qa', 'instruction': '小森田友明はいつ生まれたの?', 'input': '小森田は1981年7月10日、熊本県に生まれる。高校卒業後、2000年にJ1リーグのアビスパ福岡に入団。2001年にMFとしてデビューするも、出番は少なく、2001年シーズン終了後にJ2リーグに降格する。2002年、J2の大分トリニータに移籍。守備的MFとしてレギュラーに定着し、2002年に優勝、2003年に昇格を果たす。2005年まで多くの試合に出場した。2005年9月、J2のモンテディオ山形に移籍。2006年、J2のヴィッセル神戸へ移籍。守備的ミッドフィルダーとしてレギュラーになったものの、夏には徐々に出番が少なくなっていった。2007年、地元に本拠地を置く日本フットボールリーグのロッソ熊本(後のロアッソ熊本)へ移籍。レギュラーとして活躍し、2008年にはクラブはJ2に昇格した。その後は出場機会は限られたものの、多くの試合に出場した。2010年、インドネシアに渡り、ペルセラ・ラモンガンに移籍。2010年7月、日本に戻り、J2のギラヴァンツ北九州に入団。2012年に引退するまで、守備的ミッドフィルダーやセンターバックとして多くの試合に出場した。', 'output': '小森田友明は1981年7月10日に生まれました。'}

ローカルファイルからのロード。

# ロードするためのファイルを設置する
%%writefile sample.csv
名前,年齢,都市
山田太郎,25,東京
鈴木一郎,30,大阪
佐藤花子,22,福岡
dataset_dict = load_dataset('csv', data_files='./sample.csv')
dataset_dict
DatasetDict({
    train: Dataset({
        features: ['名前', '年齢', '都市'],
        num_rows: 3
    })
})

プログラマブルに作成。
こういうデータセットを過学習させて、訓練がうまくいくことを確認すると良い。

from datasets import Dataset
def gen():
    yield {"prompt": "何がコラじゃコラ?", "response": "何コラタココラ!"}
train_dataset = Dataset.from_generator(gen)

データセットのフィルタ

from datasets import Dataset

def gen():
    yield {"prompt": "prefix prompt1", "response": "test"}
    yield {"prompt": "prefix prompt2", "response": "test"}
    yield {"prompt": "prompt3", "response": "test"}
train_dataset = Dataset.from_generator(gen)

def filter_method(example):
    return example['prompt'].startswith('prefix')

train_dataset = train_dataset.filter(filter_method)

streaming=True

streaming=True
を指定するとデータのロードは行わず、IterableDatasetが返る。

iterable_dataset = load_dataset('kunishou/databricks-dolly-15k-ja', split='train', streaming=True)
print(iterable_dataset)
IterableDataset({
    features: ['index', 'category', 'instruction', 'input', 'output'],
    n_shards: 1
})
for data in iterable_dataset.take(5):
  print(data)
{'index': '0', 'category': 'closed_qa', 'instruction': 'ヴァージン・オーストラリア航空はいつから運航を開始したのですか?', 'input': 'ヴァージン・オーストラリア航空(Virgin Australia Airlines Pty Ltd)はオーストラリアを拠点とするヴァージン・ブランドを冠する最大の船団規模を持つ航空会社です。2000年8月31日に、ヴァージン・ブルー空港として、2機の航空機、1つの空路を運行してサービスを開始しました。2001年9月のアンセット・オーストラリア空港の崩壊後、オーストラリアの国内市場で急速に地位を確立しました。その後はブリスベン、メルボルン、シドニーをハブとして、オーストラリア国内の32都市に直接乗り入れるまでに成長しました。', 'output': 'ヴァージン・オーストラリア航空は、2000年8月31日にヴァージン・ブルー航空として、2機の航空機で単一路線の運航を開始しました。'}
{'index': '1', 'category': 'classification', 'instruction': '魚の種類はどっち?イコクエイラクブカとロープ', 'input': '', 'output': 'イコクエイラクブカ'}
{'index': '2', 'category': 'open_qa', 'instruction': 'ラクダはなぜ水なしで長く生きられるのか?', 'input': '', 'output': 'ラクダは、長時間にわたってエネルギーと水分で満たされた状態を保つために、腰の脂肪を利用しています。'}
{'index': '3', 'category': 'open_qa', 'instruction': 'アリスの両親には3人の娘がいる:エイミー、ジェシー、そして三女の名前は?', 'input': '', 'output': '三女の名前はアリス'}
{'index': '4', 'category': 'closed_qa', 'instruction': '小森田友明はいつ生まれたの?', 'input': '小森田は1981年7月10日、熊本県に生まれる。高校卒業後、2000年にJ1リーグのアビスパ福岡に入団。2001年にMFとしてデビューするも、出番は少なく、2001年シーズン終了後にJ2リーグに降格する。2002年、J2の大分トリニータに移籍。守備的MFとしてレギュラーに定着し、2002年に優勝、2003年に昇格を果たす。2005年まで多くの試合に出場した。2005年9月、J2のモンテディオ山形に移籍。2006年、J2のヴィッセル神戸へ移籍。守備的ミッドフィルダーとしてレギュラーになったものの、夏には徐々に出番が少なくなっていった。2007年、地元に本拠地を置く日本フットボールリーグのロッソ熊本(後のロアッソ熊本)へ移籍。レギュラーとして活躍し、2008年にはクラブはJ2に昇格した。その後は出場機会は限られたものの、多くの試合に出場した。2010年、インドネシアに渡り、ペルセラ・ラモンガンに移籍。2010年7月、日本に戻り、J2のギラヴァンツ北九州に入団。2012年に引退するまで、守備的ミッドフィルダーやセンターバックとして多くの試合に出場した。', 'output': '小森田友明は1981年7月10日に生まれました。'}

dataset.map

Datasetを加工して新たなDatasetを返す。
https://huggingface.co/docs/datasets/v2.16.1/en/package_reference/main_classes#datasets.Dataset.map

実行例

渡した関数の戻り値(dictionary)で、元のデータセットのレコードに属性を追加する。
元々存在する属性の場合は上書きになる。

def f(example):
    return {'add_column': f"add_culumn {example['index']}"}

mapped_dataset = dataset.map(f)
print(mapped_dataset)

print('mapped_dataset')
for data in mapped_dataset.select(range(5)):
  print(data)

# 元のデータセットは変わらない
print('dataset')
for data in dataset.select(range(5)):
  print(data)
Dataset({
    features: ['index', 'category', 'instruction', 'input', 'output', 'add_column'],
    num_rows: 15015
})
mapped_dataset
{'index': '0', 'category': 'closed_qa', 'instruction': 'ヴァージン・オーストラリア航空はいつから運航を開始したのですか?', 'input': 'ヴァージン・オーストラリア航空(Virgin Australia Airlines Pty Ltd)はオーストラリアを拠点とするヴァージン・ブランドを冠する最大の船団規模を持つ航空会社です。2000年8月31日に、ヴァージン・ブルー空港として、2機の航空機、1つの空路を運行してサービスを開始しました。2001年9月のアンセット・オーストラリア空港の崩壊後、オーストラリアの国内市場で急速に地位を確立しました。その後はブリスベン、メルボルン、シドニーをハブとして、オーストラリア国内の32都市に直接乗り入れるまでに成長しました。', 'output': 'ヴァージン・オーストラリア航空は、2000年8月31日にヴァージン・ブルー航空として、2機の航空機で単一路線の運航を開始しました。', 'add_column': 'add_culumn 0'}
{'index': '1', 'category': 'classification', 'instruction': '魚の種類はどっち?イコクエイラクブカとロープ', 'input': '', 'output': 'イコクエイラクブカ', 'add_column': 'add_culumn 1'}
{'index': '2', 'category': 'open_qa', 'instruction': 'ラクダはなぜ水なしで長く生きられるのか?', 'input': '', 'output': 'ラクダは、長時間にわたってエネルギーと水分で満たされた状態を保つために、腰の脂肪を利用しています。', 'add_column': 'add_culumn 2'}
{'index': '3', 'category': 'open_qa', 'instruction': 'アリスの両親には3人の娘がいる:エイミー、ジェシー、そして三女の名前は?', 'input': '', 'output': '三女の名前はアリス', 'add_column': 'add_culumn 3'}
{'index': '4', 'category': 'closed_qa', 'instruction': '小森田友明はいつ生まれたの?', 'input': '小森田は1981年7月10日、熊本県に生まれる。高校卒業後、2000年にJ1リーグのアビスパ福岡に入団。2001年にMFとしてデビューするも、出番は少なく、2001年シーズン終了後にJ2リーグに降格する。2002年、J2の大分トリニータに移籍。守備的MFとしてレギュラーに定着し、2002年に優勝、2003年に昇格を果たす。2005年まで多くの試合に出場した。2005年9月、J2のモンテディオ山形に移籍。2006年、J2のヴィッセル神戸へ移籍。守備的ミッドフィルダーとしてレギュラーになったものの、夏には徐々に出番が少なくなっていった。2007年、地元に本拠地を置く日本フットボールリーグのロッソ熊本(後のロアッソ熊本)へ移籍。レギュラーとして活躍し、2008年にはクラブはJ2に昇格した。その後は出場機会は限られたものの、多くの試合に出場した。2010年、インドネシアに渡り、ペルセラ・ラモンガンに移籍。2010年7月、日本に戻り、J2のギラヴァンツ北九州に入団。2012年に引退するまで、守備的ミッドフィルダーやセンターバックとして多くの試合に出場した。', 'output': '小森田友明は1981年7月10日に生まれました。', 'add_column': 'add_culumn 4'}
dataset
{'index': '0', 'category': 'closed_qa', 'instruction': 'ヴァージン・オーストラリア航空はいつから運航を開始したのですか?', 'input': 'ヴァージン・オーストラリア航空(Virgin Australia Airlines Pty Ltd)はオーストラリアを拠点とするヴァージン・ブランドを冠する最大の船団規模を持つ航空会社です。2000年8月31日に、ヴァージン・ブルー空港として、2機の航空機、1つの空路を運行してサービスを開始しました。2001年9月のアンセット・オーストラリア空港の崩壊後、オーストラリアの国内市場で急速に地位を確立しました。その後はブリスベン、メルボルン、シドニーをハブとして、オーストラリア国内の32都市に直接乗り入れるまでに成長しました。', 'output': 'ヴァージン・オーストラリア航空は、2000年8月31日にヴァージン・ブルー航空として、2機の航空機で単一路線の運航を開始しました。'}
{'index': '1', 'category': 'classification', 'instruction': '魚の種類はどっち?イコクエイラクブカとロープ', 'input': '', 'output': 'イコクエイラクブカ'}
{'index': '2', 'category': 'open_qa', 'instruction': 'ラクダはなぜ水なしで長く生きられるのか?', 'input': '', 'output': 'ラクダは、長時間にわたってエネルギーと水分で満たされた状態を保つために、腰の脂肪を利用しています。'}
{'index': '3', 'category': 'open_qa', 'instruction': 'アリスの両親には3人の娘がいる:エイミー、ジェシー、そして三女の名前は?', 'input': '', 'output': '三女の名前はアリス'}
{'index': '4', 'category': 'closed_qa', 'instruction': '小森田友明はいつ生まれたの?', 'input': '小森田は1981年7月10日、熊本県に生まれる。高校卒業後、2000年にJ1リーグのアビスパ福岡に入団。2001年にMFとしてデビューするも、出番は少なく、2001年シーズン終了後にJ2リーグに降格する。2002年、J2の大分トリニータに移籍。守備的MFとしてレギュラーに定着し、2002年に優勝、2003年に昇格を果たす。2005年まで多くの試合に出場した。2005年9月、J2のモンテディオ山形に移籍。2006年、J2のヴィッセル神戸へ移籍。守備的ミッドフィルダーとしてレギュラーになったものの、夏には徐々に出番が少なくなっていった。2007年、地元に本拠地を置く日本フットボールリーグのロッソ熊本(後のロアッソ熊本)へ移籍。レギュラーとして活躍し、2008年にはクラブはJ2に昇格した。その後は出場機会は限られたものの、多くの試合に出場した。2010年、インドネシアに渡り、ペルセラ・ラモンガンに移籍。2010年7月、日本に戻り、J2のギラヴァンツ北九州に入団。2012年に引退するまで、守備的ミッドフィルダーやセンターバックとして多くの試合に出場した。', 'output': '小森田友明は1981年7月10日に生まれました。'}

input_columns

渡した関数の引数(positional arguments)として渡す属性を指定する。

def f2(index, category):
    return {'add_column': f"add_culumn {index} {category}"}

dataset_with_input_columns = dataset.map(f2, input_columns=['index', 'category'])
print(dataset_with_input_columns)

print('dataset_with_input_columns')
for data in dataset_with_input_columns.select(range(5)):
  print(data)
Dataset({
    features: ['index', 'category', 'instruction', 'input', 'output', 'add_column'],
    num_rows: 15015
})
dataset_with_input_columns
{'index': '0', 'category': 'closed_qa', 'instruction': 'ヴァージン・オーストラリア航空はいつから運航を開始したのですか?', 'input': 'ヴァージン・オーストラリア航空(Virgin Australia Airlines Pty Ltd)はオーストラリアを拠点とするヴァージン・ブランドを冠する最大の船団規模を持つ航空会社です。2000年8月31日に、ヴァージン・ブルー空港として、2機の航空機、1つの空路を運行してサービスを開始しました。2001年9月のアンセット・オーストラリア空港の崩壊後、オーストラリアの国内市場で急速に地位を確立しました。その後はブリスベン、メルボルン、シドニーをハブとして、オーストラリア国内の32都市に直接乗り入れるまでに成長しました。', 'output': 'ヴァージン・オーストラリア航空は、2000年8月31日にヴァージン・ブルー航空として、2機の航空機で単一路線の運航を開始しました。', 'add_column': 'add_culumn 0 closed_qa'}
{'index': '1', 'category': 'classification', 'instruction': '魚の種類はどっち?イコクエイラクブカとロープ', 'input': '', 'output': 'イコクエイラクブカ', 'add_column': 'add_culumn 1 classification'}
{'index': '2', 'category': 'open_qa', 'instruction': 'ラクダはなぜ水なしで長く生きられるのか?', 'input': '', 'output': 'ラクダは、長時間にわたってエネルギーと水分で満たされた状態を保つために、腰の脂肪を利用しています。', 'add_column': 'add_culumn 2 open_qa'}
{'index': '3', 'category': 'open_qa', 'instruction': 'アリスの両親には3人の娘がいる:エイミー、ジェシー、そして三女の名前は?', 'input': '', 'output': '三女の名前はアリス', 'add_column': 'add_culumn 3 open_qa'}
{'index': '4', 'category': 'closed_qa', 'instruction': '小森田友明はいつ生まれたの?', 'input': '小森田は1981年7月10日、熊本県に生まれる。高校卒業後、2000年にJ1リーグのアビスパ福岡に入団。2001年にMFとしてデビューするも、出番は少なく、2001年シーズン終了後にJ2リーグに降格する。2002年、J2の大分トリニータに移籍。守備的MFとしてレギュラーに定着し、2002年に優勝、2003年に昇格を果たす。2005年まで多くの試合に出場した。2005年9月、J2のモンテディオ山形に移籍。2006年、J2のヴィッセル神戸へ移籍。守備的ミッドフィルダーとしてレギュラーになったものの、夏には徐々に出番が少なくなっていった。2007年、地元に本拠地を置く日本フットボールリーグのロッソ熊本(後のロアッソ熊本)へ移籍。レギュラーとして活躍し、2008年にはクラブはJ2に昇格した。その後は出場機会は限られたものの、多くの試合に出場した。2010年、インドネシアに渡り、ペルセラ・ラモンガンに移籍。2010年7月、日本に戻り、J2のギラヴァンツ北九州に入団。2012年に引退するまで、守備的ミッドフィルダーやセンターバックとして多くの試合に出場した。', 'output': '小森田友明は1981年7月10日に生まれました。', 'add_column': 'add_culumn 4 closed_qa'}

remove_columns

削除する属性を指定する。

def f3(example):
    return {'add_column': f"add_culumn {example['index']}"}

dataset_with_remove_columns = dataset.map(f3, remove_columns=['category', 'instruction'])
print(dataset_with_remove_columns)

print('dataset_with_remove_columns')
for data in dataset_with_remove_columns.select(range(5)):
  print(data)
Dataset({
    features: ['index', 'input', 'output', 'add_column'],
    num_rows: 15015
})
dataset_with_remove_columns
{'index': '0', 'input': 'ヴァージン・オーストラリア航空(Virgin Australia Airlines Pty Ltd)はオーストラリアを拠点とするヴァージン・ブランドを冠する最大の船団規模を持つ航空会社です。2000年8月31日に、ヴァージン・ブルー空港として、2機の航空機、1つの空路を運行してサービスを開始しました。2001年9月のアンセット・オーストラリア空港の崩壊後、オーストラリアの国内市場で急速に地位を確立しました。その後はブリスベン、メルボルン、シドニーをハブとして、オーストラリア国内の32都市に直接乗り入れるまでに成長しました。', 'output': 'ヴァージン・オーストラリア航空は、2000年8月31日にヴァージン・ブルー航空として、2機の航空機で単一路線の運航を開始しました。', 'add_column': 'add_culumn 0'}
{'index': '1', 'input': '', 'output': 'イコクエイラクブカ', 'add_column': 'add_culumn 1'}
{'index': '2', 'input': '', 'output': 'ラクダは、長時間にわたってエネルギーと水分で満たされた状態を保つために、腰の脂肪を利用しています。', 'add_column': 'add_culumn 2'}
{'index': '3', 'input': '', 'output': '三女の名前はアリス', 'add_column': 'add_culumn 3'}
{'index': '4', 'input': '小森田は1981年7月10日、熊本県に生まれる。高校卒業後、2000年にJ1リーグのアビスパ福岡に入団。2001年にMFとしてデビューするも、出番は少なく、2001年シーズン終了後にJ2リーグに降格する。2002年、J2の大分トリニータに移籍。守備的MFとしてレギュラーに定着し、2002年に優勝、2003年に昇格を果たす。2005年まで多くの試合に出場した。2005年9月、J2のモンテディオ山形に移籍。2006年、J2のヴィッセル神戸へ移籍。守備的ミッドフィルダーとしてレギュラーになったものの、夏には徐々に出番が少なくなっていった。2007年、地元に本拠地を置く日本フットボールリーグのロッソ熊本(後のロアッソ熊本)へ移籍。レギュラーとして活躍し、2008年にはクラブはJ2に昇格した。その後は出場機会は限られたものの、多くの試合に出場した。2010年、インドネシアに渡り、ペルセラ・ラモンガンに移籍。2010年7月、日本に戻り、J2のギラヴァンツ北九州に入団。2012年に引退するまで、守備的ミッドフィルダーやセンターバックとして多くの試合に出場した。', 'output': '小森田友明は1981年7月10日に生まれました。', 'add_column': 'add_culumn 4'}

batched=True

渡した関数に複数のデータが渡される様になる。
引数のデータ形式が変わるので注意。
戻り値のデータ形式は変わらない。

def f4(batched_example):
    print('batched_example')
    print(batched_example)
    return {'add_column': [ f"add_culumn {index}" for index in batched_example['index'] ]}

mapped_dataset = dataset.select(range(5)).map(f4, batched=True, batch_size=2)
print(mapped_dataset)

print('mapped_dataset')
for data in mapped_dataset:
  print(data)
Dataset({
    features: ['index', 'category', 'input', 'instruction', 'output', 'add_column'],
    num_rows: 5
})
mapped_dataset
{'index': '0', 'category': 'closed_qa', 'input': 'ヴァージン・オーストラリア航空(Virgin Australia Airlines Pty Ltd)はオーストラリアを拠点とするヴァージン・ブランドを冠する最大の船団規模を持つ航空会社です。2000年8月31日に、ヴァージン・ブルー空港として、2機の航空機、1つの空路を運行してサービスを開始しました。2001年9月のアンセット・オーストラリア空港の崩壊後、オーストラリアの国内市場で急速に地位を確立しました。その後はブリスベン、メルボルン、シドニーをハブとして、オーストラリア国内の32都市に直接乗り入れるまでに成長しました。', 'instruction': 'ヴァージン・オーストラリア航空はいつから運航を開始したのですか?', 'output': 'ヴァージン・オーストラリア航空は、2000年8月31日にヴァージン・ブルー航空として、2機の航空機で単一路線の運航を開始しました。', 'add_column': 'add_culumn 0'}
{'index': '1', 'category': 'classification', 'input': '', 'instruction': '魚の種類はどっち?イコクエイラクブカとロープ', 'output': 'イコクエイラクブカ', 'add_column': 'add_culumn 1'}
{'index': '2', 'category': 'open_qa', 'input': '', 'instruction': 'ラクダはなぜ水なしで長く生きられるのか?', 'output': 'ラクダは、長時間にわたってエネルギーと水分で満たされた状態を保つために、腰の脂肪を利用しています。', 'add_column': 'add_culumn 2'}
{'index': '3', 'category': 'open_qa', 'input': '', 'instruction': 'アリスの両親には3人の娘がいる:エイミー、ジェシー、そして三女の名前は?', 'output': '三女の名前はアリス', 'add_column': 'add_culumn 3'}
{'index': '4', 'category': 'closed_qa', 'input': '小森田は1981年7月10日、熊本県に生まれる。高校卒業後、2000年にJ1リーグのアビスパ福岡に入団。2001年にMFとしてデビューするも、出番は少なく、2001年シーズン終了後にJ2リーグに降格する。2002年、J2の大分トリニータに移籍。守備的MFとしてレギュラーに定着し、2002年に優勝、2003年に昇格を果たす。2005年まで多くの試合に出場した。2005年9月、J2のモンテディオ山形に移籍。2006年、J2のヴィッセル神戸へ移籍。守備的ミッドフィルダーとしてレギュラーになったものの、夏には徐々に出番が少なくなっていった。2007年、地元に本拠地を置く日本フットボールリーグのロッソ熊本(後のロアッソ熊本)へ移籍。レギュラーとして活躍し、2008年にはクラブはJ2に昇格した。その後は出場機会は限られたものの、多くの試合に出場した。2010年、インドネシアに渡り、ペルセラ・ラモンガンに移籍。2010年7月、日本に戻り、J2のギラヴァンツ北九州に入団。2012年に引退するまで、守備的ミッドフィルダーやセンターバックとして多くの試合に出場した。', 'instruction': '小森田友明はいつ生まれたの?', 'output': '小森田友明は1981年7月10日に生まれました。', 'add_column': 'add_culumn 4'}

tokenizer.init

トーカナイザの初期化。
実際には以下のようなtokenizer_config.jsonを使って初期化が行われる。
https://huggingface.co/tokyotech-llm/Swallow-7b-instruct-hf/blob/main/tokenizer_config.json

実行例

import torch
from transformers import AutoTokenizer
model_name = "tokyotech-llm/Swallow-7b-instruct-hf"
tokenizer = AutoTokenizer.from_pretrained(model_name)

# ValueError: Asking to pad but the tokenizer does not have a padding token. Please select a token to use as `pad_token` `(tokenizer.pad_token = tokenizer.eos_token e.g.)` or add a new pad token via `tokenizer.add_special_tokens({'pad_token': '[PAD]'})`.
# に対応
tokenizer.pad_token = tokenizer.eos_token

tokenizerの属性

一般的な属性

print('is_fast')
print(tokenizer.is_fast)

print('padding_side')
print(tokenizer.padding_side)

print('truncation_side')
print(tokenizer.truncation_side)
is_fast
True
padding_side
right
truncation_side
right

スペシャルトークン関連

bos_token: beginning of a sentence
eos_token: end of a sentence
unk_token: out-of-vocabulary token
sep_token: separating two different sentences in the same input
pad_token: make arrays of tokens the same size for batching purpose
cls_token: class of the input (used by BERT for instance)
mask_token: masked token (used by masked-language modeling pretraining objectives, like BERT)

print('all_special_tokens')
print(tokenizer.all_special_tokens)

print('all_special_ids')
print(tokenizer.all_special_ids)

print('special_tokens_map')
print(tokenizer.special_tokens_map)

print('additional_special_tokens')
print(tokenizer.additional_special_tokens)
all_special_tokens
['<s>', '</s>', '<unk>']
all_special_ids
[1, 2, 0]
special_tokens_map
{'bos_token': '<s>', 'eos_token': '</s>', 'unk_token': '<unk>'}
additional_special_tokens
[]

tokenizer.call

テキストをトークンIDに変換する。

実行例

batch_encoding = tokenizer('あいうえおかきくけこ')
print(type(batch_encoding))
print(batch_encoding)

decoded_str = tokenizer.decode(batch_encoding["input_ids"])
print(decoded_str)
<class 'transformers.tokenization_utils_base.BatchEncoding'>
{'input_ids': [1, 29871, 30641, 32010, 30914, 33242, 30538, 30568, 30807, 30589], 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]}
<s> あいうえおかきくけこ

複数文字列にも対応。

batch_encoding = tokenizer(['あいうえおかきくけこ', 'さしすせそ'])
print(batch_encoding)

decoded_str = tokenizer.decode(batch_encoding["input_ids"][0])
print(decoded_str)

decoded_str = tokenizer.decode(batch_encoding["input_ids"][1])
print(decoded_str)
{'input_ids': [[1, 29871, 30641, 32010, 30914, 33242, 30538, 30568, 30807, 30589], [1, 29871, 41059, 30427, 31095, 31110]], 'attention_mask': [[1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1]]}
<s> あいうえおかきくけこ
<s> さしすせそ

padding

最長のトークン長でパディング
padding=True
でも同じ挙動になる。

batch_encoding = tokenizer(['あいうえおかきくけこ', 'さしすせそ'], padding='longest')
print(batch_encoding)

print(len(batch_encoding["input_ids"][0]))
decoded_str = tokenizer.decode(batch_encoding["input_ids"][0])
print(decoded_str)

print(len(batch_encoding["input_ids"][1]))
decoded_str = tokenizer.decode(batch_encoding["input_ids"][1])
print(decoded_str)
{'input_ids': [[1, 29871, 30641, 32010, 30914, 33242, 30538, 30568, 30807, 30589], [1, 29871, 41059, 30427, 31095, 31110, 2, 2, 2, 2]], 'attention_mask': [[1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 0, 0, 0, 0]]}
10
<s> あいうえおかきくけこ
10
<s> さしすせそ</s></s></s></s>

max_lengthでパディング

batch_encoding = tokenizer(['あいうえおかきくけこ', 'さしすせそ'], padding='max_length', max_length=24)
print(batch_encoding)

print(len(batch_encoding["input_ids"][0]))
decoded_str = tokenizer.decode(batch_encoding["input_ids"][0])
print(decoded_str)

print(len(batch_encoding["input_ids"][1]))
decoded_str = tokenizer.decode(batch_encoding["input_ids"][1])
print(decoded_str)
{'input_ids': [[1, 29871, 30641, 32010, 30914, 33242, 30538, 30568, 30807, 30589, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2], [1, 29871, 41059, 30427, 31095, 31110, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]], 'attention_mask': [[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]}
24
<s> あいうえおかきくけこ</s></s></s></s></s></s></s></s></s></s></s></s></s></s>
24
<s> さしすせそ</s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s>

truncation

max_lengthで切り捨て
max_lengthの指定がない場合、モデルが受け付ける最大長で切り捨てられる。

# ValueError: Asking to pad but the tokenizer does not have a padding token. Please select a token to use as `pad_token` `(tokenizer.pad_token = tokenizer.eos_token e.g.)` or add a new pad token via `tokenizer.add_special_tokens({'pad_token': '[PAD]'})`.
# に対応
tokenizer.pad_token = tokenizer.eos_token

batch_encoding = tokenizer(['あいうえおかきくけこ', 'さしすせそ'], truncation='longest_first', max_length=8)
print(batch_encoding)

print(len(batch_encoding["input_ids"][0]))
decoded_str = tokenizer.decode(batch_encoding["input_ids"][0])
print(decoded_str)

print(len(batch_encoding["input_ids"][1]))
decoded_str = tokenizer.decode(batch_encoding["input_ids"][1])
print(decoded_str)
{'input_ids': [[1, 29871, 30641, 32010, 30914, 33242, 30538, 30568], [1, 29871, 41059, 30427, 31095, 31110]], 'attention_mask': [[1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1]]}
8
<s> あいうえおかきく
6
<s> さしすせそ

質問と回答のような形式の場合に質問の方を切り捨てる。

batch_encoding = tokenizer(['あいうえおかきくけこ', 'さしすせそ'], ['アイウエオカキクケコ', 'サシスセソ'], truncation='only_first', max_length=15)
print(batch_encoding)

print(len(batch_encoding["input_ids"][0]))
decoded_str = tokenizer.decode(batch_encoding["input_ids"][0])
print(decoded_str)

print(len(batch_encoding["input_ids"][1]))
decoded_str = tokenizer.decode(batch_encoding["input_ids"][1])
print(decoded_str)
{'input_ids': [[1, 29871, 30641, 32010, 30914, 33242, 1, 29871, 32230, 37288, 41717, 30454, 30305, 30978, 30459], [1, 29871, 41059, 30427, 31095, 31110, 1, 29871, 30615, 37096, 30885, 31051]], 'attention_mask': [[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]]}
15
<s> あいうえおか<s> アイウエオカキクケコ
12
<s> さしすせそ<s> サシスセソ

質問と回答のような形式の場合に回答の方を切り捨てる。

batch_encoding = tokenizer(['あいうえおかきくけこ', 'さしすせそ'], ['アイウエオカキクケコ', 'サシスセソ'], truncation='only_second', max_length=15)
print(batch_encoding)

print(len(batch_encoding["input_ids"][0]))
decoded_str = tokenizer.decode(batch_encoding["input_ids"][0])
print(decoded_str)

print(len(batch_encoding["input_ids"][1]))
decoded_str = tokenizer.decode(batch_encoding["input_ids"][1])
print(decoded_str)
{'input_ids': [[1, 29871, 30641, 32010, 30914, 33242, 30538, 30568, 30807, 30589, 1, 29871, 32230, 37288, 41717], [1, 29871, 41059, 30427, 31095, 31110, 1, 29871, 30615, 37096, 30885, 31051]], 'attention_mask': [[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]]}
15
<s> あいうえおかきくけこ<s> アイウエオカ
12
<s> さしすせそ<s> サシスセソ

return_overflowing_tokens=True

truncation=True
時にmax_lengthから溢れたトークンを分割して返す。

batch_encoding = tokenizer(['あいうえおかきくけこ', 'さしすせそ'], truncation=True, max_length=8, return_overflowing_tokens=True)
print(batch_encoding)

print(len(batch_encoding["input_ids"][0]))
decoded_str = tokenizer.decode(batch_encoding["input_ids"][0])
print(decoded_str)

print(len(batch_encoding["input_ids"][1]))
decoded_str = tokenizer.decode(batch_encoding["input_ids"][1])
print(decoded_str)

print(len(batch_encoding["input_ids"][2]))
decoded_str = tokenizer.decode(batch_encoding["input_ids"][2])
print(decoded_str)
{'input_ids': [[1, 29871, 30641, 32010, 30914, 33242, 30538, 30568], [1, 30807, 30589], [1, 29871, 41059, 30427, 31095, 31110]], 'attention_mask': [[1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1], [1, 1, 1, 1, 1, 1]], 'overflow_to_sample_mapping': [0, 0, 1]}
8
<s> あいうえおかきく
3
<s>けこ
6
<s> さしすせそ

return_tensors

tensors形式で返す。
padding等でトークン長を揃える必要がある。

batch_encoding = tokenizer(['あいうえおかきくけこ', 'さしすせそ'], padding='max_length', max_length=24, return_tensors='pt')
import pprint
pprint.pprint(batch_encoding)

decoded_str = tokenizer.decode(batch_encoding["input_ids"][0])
print(decoded_str)

decoded_str = tokenizer.decode(batch_encoding["input_ids"][1])
print(decoded_str)
{'attention_mask': tensor([[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]),
 'input_ids': tensor([[    1, 29871, 30641, 32010, 30914, 33242, 30538, 30568, 30807, 30589,
             2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
             2,     2,     2,     2],
        [    1, 29871, 41059, 30427, 31095, 31110,     2,     2,     2,     2,
             2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
             2,     2,     2,     2]])}
<s> あいうえおかきくけこ</s></s></s></s></s></s></s></s></s></s></s></s></s></s>
<s> さしすせそ</s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s>

DataCollator

1バッチ分のdatasetsを加工してモデルに対するinputsを返す。
https://huggingface.co/docs/transformers/main_classes/data_collator

実行方法(DefaultDataCollator)

from transformers import DefaultDataCollator

max_length = 24
batch_encoding = tokenizer('あいうえおかきくけこ', padding='max_length', max_length=max_length)
print(batch_encoding)

batch_encoding2 = tokenizer('さしすせそ', padding='max_length', max_length=max_length)
print(batch_encoding2)

data_collator = DefaultDataCollator()
batch = data_collator([batch_encoding, batch_encoding2])
print(batch)
{'input_ids': [1, 29871, 30641, 32010, 30914, 33242, 30538, 30568, 30807, 30589, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2], 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]}
{'input_ids': [1, 29871, 41059, 30427, 31095, 31110, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2], 'attention_mask': [1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]}
{'input_ids': tensor([[    1, 29871, 30641, 32010, 30914, 33242, 30538, 30568, 30807, 30589,
             2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
             2,     2,     2,     2],
        [    1, 29871, 41059, 30427, 31095, 31110,     2,     2,     2,     2,
             2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
             2,     2,     2,     2]]), 'attention_mask': tensor([[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]])}

以下のように、複数文字列を渡したときに返されるbatch_encodingをdata_collatorに渡す方法がちょっとわかりませんでした。
tensorのネストが1つ深くなってしまっています。

batch_encoding = tokenizer(['あいうえおかきくけこ', 'さしすせそ'], padding='max_length', max_length=24)
print(batch_encoding)

data_collator = DefaultDataCollator()
batch = data_collator([batch_encoding])
print(batch)
{'input_ids': [[1, 29871, 30641, 32010, 30914, 33242, 30538, 30568, 30807, 30589, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2], [1, 29871, 41059, 30427, 31095, 31110, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]], 'attention_mask': [[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]}
{'input_ids': tensor([[[    1, 29871, 30641, 32010, 30914, 33242, 30538, 30568, 30807, 30589,
              2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
              2,     2,     2,     2],
         [    1, 29871, 41059, 30427, 31095, 31110,     2,     2,     2,     2,
              2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
              2,     2,     2,     2]]]), 'attention_mask': tensor([[[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
          0],
         [1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
          0]]])}

DataCollatorWithPadding

データのpaddingを行う。
tokenizerでも同様の処理を行えますが、出来ればこちらでやったほうが良いように思えました。

https://huggingface.co/docs/transformers/main_classes/data_collator#transformers.DataCollatorWithPadding

from transformers import DataCollatorWithPadding

batch_encoding = tokenizer('あいうえおかきくけこ')
print(batch_encoding)

batch_encoding2 = tokenizer('さしすせそ')
print(batch_encoding2)

max_length = 24
data_collator = DataCollatorWithPadding(tokenizer, padding='max_length', max_length=max_length)
batch = data_collator([batch_encoding, batch_encoding2])
print(batch)

decoded_str = tokenizer.decode(batch["input_ids"][0])
print(decoded_str)

decoded_str2 = tokenizer.decode(batch["input_ids"][1])
print(decoded_str2)
{'input_ids': [1, 29871, 30641, 32010, 30914, 33242, 30538, 30568, 30807, 30589], 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]}
{'input_ids': [1, 29871, 41059, 30427, 31095, 31110], 'attention_mask': [1, 1, 1, 1, 1, 1]}
{'input_ids': tensor([[    1, 29871, 30641, 32010, 30914, 33242, 30538, 30568, 30807, 30589,
             2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
             2,     2,     2,     2],
        [    1, 29871, 41059, 30427, 31095, 31110,     2,     2,     2,     2,
             2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
             2,     2,     2,     2]]), 'attention_mask': tensor([[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]])}
<s> あいうえおかきくけこ</s></s></s></s></s></s></s></s></s></s></s></s></s></s>
<s> さしすせそ</s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s>

DataCollatorForSeq2Seq

input_idsだけでなくlabelsに対してもパディングを行う。
と書いてあるが、結果はlabelsの方にパディングは入っていない。(tokenizerするフェーズで対応する必要がありそう)
https://huggingface.co/docs/transformers/main_classes/data_collator#transformers.DataCollatorForSeq2Seq

from transformers import DataCollatorForSeq2Seq
from datasets import Dataset

def f(example):
    tokenized = tokenizer(example['text'])
    tokenized['labels'] = tokenized['input_ids'].copy()
    return tokenized

dataset = Dataset.from_dict({"text": ["あいうえおかきくけこ", "さしすせそ"]})
mapped_dataset = dataset.map(f, remove_columns=['text'])
print(mapped_dataset)

max_length = 24
data_collator = DataCollatorForSeq2Seq(tokenizer, padding='max_length', max_length=max_length)
batch = data_collator(list(mapped_dataset))
print(batch)

decoded_str = tokenizer.decode(batch["input_ids"][0])
print(decoded_str)

decoded_str2 = tokenizer.decode(batch["input_ids"][1])
print(decoded_str2)
Dataset({
    features: ['input_ids', 'attention_mask', 'labels'],
    num_rows: 2
})
{'input_ids': tensor([[    1, 29871, 30641, 32010, 30914, 33242, 30538, 30568, 30807, 30589,
             2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
             2,     2,     2,     2],
        [    1, 29871, 41059, 30427, 31095, 31110,     2,     2,     2,     2,
             2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
             2,     2,     2,     2]]), 'attention_mask': tensor([[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]), 'labels': tensor([[    1, 29871, 30641, 32010, 30914, 33242, 30538, 30568, 30807, 30589],
        [    1, 29871, 41059, 30427, 31095, 31110,  -100,  -100,  -100,  -100]])}
<s> あいうえおかきくけこ</s></s></s></s></s></s></s></s></s></s></s></s></s></s>
<s> さしすせそ</s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s></s>

DataCollatorForLanguageModeling

mlm=Trueにすると単語の一部をマスクすることができる。
labelsの設定が自動で行われる。
最大長でパディングされる。padding_lengthは指定できない。

https://huggingface.co/docs/transformers/main_classes/data_collator#transformers.DataCollatorForLanguageModeling

from transformers import DataCollatorForLanguageModeling
from datasets import Dataset

def f(example):
    tokenized = tokenizer(example['text'])
    return tokenized

dataset = Dataset.from_dict({"text": ["あいうえおかきくけこ", "さしすせそ"]})
mapped_dataset = dataset.map(f, remove_columns=['text'])
print(mapped_dataset)

data_collator = DataCollatorForLanguageModeling(tokenizer, mlm=False)
batch = data_collator(list(mapped_dataset))
print(batch)

decoded_str = tokenizer.decode(batch["input_ids"][0])
print(decoded_str)

decoded_str2 = tokenizer.decode(batch["input_ids"][1])
print(decoded_str2)
Dataset({
    features: ['input_ids', 'attention_mask'],
    num_rows: 2
})
{'input_ids': tensor([[    1, 29871, 30641, 32010, 30914, 33242, 30538, 30568, 30807, 30589],
        [    1, 29871, 41059, 30427, 31095, 31110,     2,     2,     2,     2]]), 'attention_mask': tensor([[1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
        [1, 1, 1, 1, 1, 1, 0, 0, 0, 0]]), 'labels': tensor([[    1, 29871, 30641, 32010, 30914, 33242, 30538, 30568, 30807, 30589],
        [    1, 29871, 41059, 30427, 31095, 31110,  -100,  -100,  -100,  -100]])}
<s> あいうえおかきくけこ
<s> さしすせそ</s></s></s></s>

その他

何か試したい場合は、こちらのリンクからnotebookをコピーして使ってください。
https://colab.research.google.com/drive/18fVwqMO2JiqR32mCMQppH9MruOSXimU4?usp=sharing

Discussion