2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Transformersを使ったローカルLLM構築②:pipelineの裏側を解剖!トークン化からデコードまで完全理解

Posted at

前回のおさらい

前回の記事では、Transformersのpipeline()関数を用いて文章生成を実践しました。
今回はpipeline()関数の中身を分解して、自然言語処理の各プロセスを再現します!

pipelineの内部処理を理解する

pipeline()関数の裏側で起こっていることを理解する上で以下の図が分かりやすかったです。
"sentiment-analysis"(感情分析)を例に各プロセスの概要が記載されています。

image.png

引用:https://huggingface.co/learn/llm-course/ja/chapter2/2

大きく分けて3段階あり、各フェーズでの「入力」と「出力」が何かを意識すると理解しやすいです。

段階 担当 (コンポーネント) 役割
前処理 トークナイザ (Tokenizer) 生データ(Raw text)をモデルが処理できるテンソル(Input IDs)に変換
モデル実行 モデル (Model) 数値化されたデータからタスク(分類や要約など)に合わせた生のスコアを出力する(Logits)
後処理 後処理プロセス 生の数値を、人間が理解できる形式(確率、ラベル等)に変換する(Preconditions)

(補足)テンソルとは?

あまり聞きなじみのない単語としてテンソルが出てきましたが、ベクトルや行列のような多次元のデータ構造をイメージしてもらえれば一旦は読み進められるかと思います。

↓こちらの説明が分かりやすいです。

テンソルは、スカラーやベクトル、行列のような数学的な概念を一般化した多次元のデータ構造です。スカラーは0次元テンソル(0階テンソル)と見なせますし、ベクトルは1次元テンソル(1階テンソル)、行列は2次元テンソル(2階テンソル)、さらにはn次元データ構造ではn次元テンソル(n階テンソル)となります。より高次元のデータを扱う際には、テンソルが非常に便利です。データが2つ以上の次元を持つ場合、それはテンソルとして表現できます。
https://zero2one.jp/learningblog/machine-learning-matrix/?srsltid=AfmBOornuM1V3x2Ki2knP5RawL20jJRBuWGerNvX7xJX29K_erLz-7Br

実践:各プロセスを分解して実装

Transformersを用いることで、トークナイザとモデルをそれぞれ分けて実装することができます。
前回実行したpipelineでのtext-generationと同内容を内部プロセスを分解して実装してみます。

プロセス

  1. 前処理
    1. トークン分割: 生データ(テキスト)を分解し、 トークン(文字列の配列)に変換
    2. トークンID化: トークンを割り当てられたIDに変換
    3. テンソル化: IDの配列をテンソル形式へ
  2. モデル実行 
    • 文書生成処理
  3. 後処理
    • 生成結果をテキストにデコード

コード

前回に引き続き、以下の前提で実施します。
実行内容:文章生成
使用モデル:Qwen/Qwen3-0.6B
PyTorchを使用

与えるプロンプトは以下にします。
トークン化の検証のため、専門用語Transformersや記号" ! を含む形にしています。

The "Transformers" library is amazing!

コード全量

from transformers import AutoTokenizer, AutoModelForCausalLM
import torch

model_id = "Qwen/Qwen3-0.6B"

prompt = "The \"Transformers\" library is amazing!"

# 1.前処理
# トークナイザーのロード
tokenizer = AutoTokenizer.from_pretrained(model_id)

# 1-1.トークン化: テキスト → トークン(文字列の配列)
tokens = tokenizer.tokenize(prompt)
print("1-1. After tokenization (tokens):\n", tokens)


# 1-2.トークンID化: トークン → ID(整数の配列)
token_ids = tokenizer.convert_tokens_to_ids(tokens)
print("1-2. After token ID conversion:\n", token_ids)


# 1-3.テンソル化: ID → テンソル
inputs = {
    'input_ids': torch.tensor([token_ids]),
    'attention_mask': torch.tensor([[1] * len(token_ids)])
}
print("1-3. After tensorization:\n", inputs)

# 2.モデル実行: テンソル → テンソル
model = AutoModelForCausalLM.from_pretrained(
    model_id,
)
outputs = model.generate(
    **inputs,
    max_new_tokens=50,
    temperature=0.7,
    do_sample=True
)
print("2. After generation:\n", outputs)


# 3.後処理: テンソル → テキスト
text = tokenizer.decode(outputs[0], skip_special_tokens=True)
print("3. After decoding:\n", text)

出力結果

1-1. After tokenization (tokens):
 ['The', 'Ġ"', 'Transform', 'ers', '"', 'Ġlibrary', 'Ġis', 'Ġamazing', '!']
1-2. After token ID conversion:
 [785, 330, 8963, 388, 1, 6733, 374, 7897, 0]
1-3. After tensorization:
 {'input_ids': tensor([[ 785,  330, 8963,  388,    1, 6733,  374, 7897,    0]]), 'attention_mask': tensor([[1, 1, 1, 1, 1, 1, 1, 1, 1]])}
2. After generation:
 tensor([[ 785,  330, 8963,  388,    1, 6733,  374, 7897,    0, 1084, 6147, 3847,
          311, 6979, 2526, 6846,  369, 6171,   13, 1988, 1246,  646,  358,  990,
          419, 6733,  311, 4211,  264,  536,  448,  264, 1714,  429, 4990,  264,
         3890, 1372,  315, 5977,   30,  358, 1184,  311, 1855,  264,  536,  429,
          646, 1896, 5248, 5977,  311,  264, 1714,   11,  323,  429, 1714]])
3. After decoding:
 The "Transformers" library is amazing! It allows users to define custom classes for objects. But how can I use this library to implement a class with a method that takes a variable number of arguments? I need to create a class that can take multiple arguments to a method, and that method


最終的に生成したテキストが得られていることが分かります。
それでは各処理の詳細を確認していきます!

1. 前処理

事前準備として、Tokenizerを用意します。

model_id = "Qwen/Qwen3-0.6B"
tokenizer = AutoTokenizer.from_pretrained(model_id)

AutoTokenizer.from_pretrained()メソッドを使用し、モデルのチェックポイント名(識別子)を渡すことで、Model Hubからそのモデル専用のトークナイザ設定を自動的にダウンロードできます。

1-1. トークン分割

まずは、生データのテキストを単語、サブワード、または記号(句読点など)に分割します。これら分割された個々の単位を「トークン」と呼びます。

prompt = "The \"Transformers\" library is amazing!"

# 1-1.トークン化: テキスト → トークン(文字列の配列)
tokens = tokenizer.tokenize(prompt)
print("1-1. After tokenization (tokens):", tokens)

出力結果

1-1. After tokenization (tokens):
 ['The', 'Ġ"', 'Transform', 'ers', '"', 'Ġlibrary', 'Ġis', 'Ġamazing', '!']

実際の結果を確認しましょう。Ġが含まれているので戸惑うかもしれませんが、Ġは空白を識別する記号になります。
なので、
The " Transform ers " library is amazing !
と分割されていることが分かります。

基本的には単語や記号単位で分割されていますが、TransformersTransformersに分割されています。
これは「頻繁に登場する単語はそのまま残し、あまり使われない単語は意味のある小さな単位(サブワード)に分割する」という原則に基づいて「サブワード」に分割されています。この手法によって合成語の意味を効率的に表現できるようになっています。

1-2. トークンをID化

トークナイザが保持する語彙(Vocabulary)と照らし合わせて、各語彙に対応するIDの紐づけを行います。

# 1-2.トークンID化: トークン → ID(整数の配列)
token_ids = tokenizer.convert_tokens_to_ids(tokens)
print("1-2. After token ID conversion:\n", token_ids)

出力結果

1-2. After token ID conversion:
 [785, 330, 8963, 388, 1, 6733, 374, 7897, 0]

前段階で9個に分割されたトークン群 The " Transform ers " library is amazing ! が、
処理後は9つの整数 [785, 330, 8963, 388, 1, 6733, 374, 7897, 0]に変換されたことが分かります。

1-3. テンソル化

IDの配列をモデルが処理できるテンソルに変換します。

inputs = {
    'input_ids': torch.tensor([token_ids]),
    'attention_mask': torch.tensor([[1] * len(token_ids)])
}
print("1-3. After tensorization:\n", inputs)

出力結果

1-3. After tensorization:
 {'input_ids': tensor([[ 785,  330, 8963,  388,    1, 6733,  374, 7897,    0]]), 'attention_mask': tensor([[1, 1, 1, 1, 1, 1, 1, 1, 1]])}

input_idsattention_maskの2つの要素で構成されています。

  • input_ids: 前段階で作成したトークンIDの配列
  • attention_mask: どのトークンに注目(アテンション)すべきか、どのトークンを無視すべきかを伝えるためのバイナリデータ。今回は対象の文章が1つのため、すべて1(無視しない)が指定されています。

(補足)複数の文章を同時に処理する際、短い文章を長い文章の長さに合わせるために「パディング(0などの無意味な値での埋め合わせ)」が行われます。その際パディング部を無視するために対応箇所にattention_mask=0が設定されます。

2. モデル実行

実際に処理をするモデルを呼び出して、テキスト生成を指示します。

model = AutoModelForCausalLM.from_pretrained(
    model_id,
)
outputs = model.generate(
    **inputs,
    max_new_tokens=50,
    temperature=0.7,
    do_sample=True
)
print("2. After generation:\n", outputs)

AutoModelForCausalLMという「次の単語を予測して文章を生成する」タスクに特化した、自動選択型のモデルローダーを使用しました。
文章生成時のパラメータ(max_new_tokens, temperature)は前回と同様にしています。

出力結果

2. After generation:
 tensor([[ 785,  330, 8963,  388,    1, 6733,  374, 7897,    0, 1084, 6147, 3847,
          311, 6979, 2526, 6846,  369, 6171,   13, 1988, 1246,  646,  358,  990,
          419, 6733,  311, 4211,  264,  536,  448,  264, 1714,  429, 4990,  264,
         3890, 1372,  315, 5977,   30,  358, 1184,  311, 1855,  264,  536,  429,
          646, 1896, 5248, 5977,  311,  264, 1714,   11,  323,  429, 1714]])

モデルの生成結果もテンソル形式になっています。
今回は単一の文章を処理しているので第一次元の要素は1、第二次元の要素は生成したトークン数57になっています。

生成したトークンID群についてみてみると、
冒頭の7つのトークンは書き出しの部分("The "Transformers" library is amazing!")なので、1-2で変換した結果と一致しています。

# 冒頭:与えたプロンプトに対応する部分
[785, 330, 8963, 388, 1, 6733, 374]

以降の50個トークンはモデルが新規生成したトークンになります。
作成時に指定したパラメータmax_new_tokens=50の通り、50個のトークン生成を実行していることが分かりました!

# 続きの文章
[7897, 0, 1084, 6147, 3847, ...]

3. 後処理: デコード

前段階で作成したID列からデコードすることで、人間が認識できる言語に変換します。

# 3.後処理: テンソル → テキスト
text = tokenizer.decode(outputs[0], skip_special_tokens=True)
print("3. After decoding:\n", text)

skip_special_tokensをTrueに指定することで、特殊トークン(モデルが内部的に使用する制御用のトークン)を出力結果から除外しています。

出力結果

3. After decoding:
 The "Transformers" library is amazing! It allows users to define custom classes for objects. But how can I use this library to implement a class with a method that takes a variable number of arguments? I need to create a class that can take multiple arguments to a method, and that method

 (訳)「Transformers」ライブラリは素晴らしいです!オブジェクト用のカスタムクラスを定義できます。しかし、このライブラリを使って可変長引数を取るメソッドを持つクラスを実装するにはどうすればよいでしょうか?メソッドに複数の引数を受け取れるクラスを作成する必要があり、そのメソッドは

※ 翻訳はDeepL翻訳を使用しました。

前回はpipeline()で完結していた処理を分解して、同様の処理を再現することができました!
各フェーズでの出力結果に着目することで、各プロセスが情報をどのように処理しているか把握しやすかったです。

まとめ

今回は、Transformersのpipeline()関数によるtext-generation(文章生成)の内部処理を分解して実装しました。

  • 前処理(Tokenizer)

    • トークン分割: テキストを単語・サブワード・記号に分割
    • トークンID化: 各トークンを語彙(Vocabulary)内のIDに変換
    • テンソル化: IDの配列をモデルが処理できる形式に変換
  • モデル実行

    • AutoModelForCausalLMで文章生成タスクを実行
    • 入力プロンプト部分 + 新規生成部分を含むテンソルを出力
  • 後処理(Decoding)

    • トークンIDを人間が読めるテキストに変換

ポイント

  • pipeline()は便利だが、内部処理を理解することで柔軟なカスタマイズが可能
  • トークナイザとモデルはセットで使用する(モデル専用のトークナイザを使用)
  • 各プロセスの入出力の形式(テキスト → トークン → ID → テンソル → テキスト)を理解することが重要

次回は、temperaturetop-p top-k といった主要なパラメータの意味と使い分けについて解説・実践する予定です!

作成コード

利用環境もこちら参照
https://github.com/abi-inami/Transformers-sample

翻訳ツール

参考

2
0
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
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?