word_idsの代わりの関数を作ってみる

## はじめに
最近自然言語処理はじめました :muscle:

[HuggingFaceのチュートリアル](https://huggingface.co/learn/nlp-course/ja/chapter7/3?fw=tf)をやってたのですが、モデルのトークナイザーにis_fast属性（高速トークナイザー）がなかったので作ることになりました。
※引用ばかりですみません
## word_idsとは？
自然言語処理でモデルにデータを渡す際、文毎の位置を0から割り当てたID
サブワードの分割では一つの単語として同じ数字が割り当てられる
## input_idsとの違い
Claude.ai
>input_idsとword_idsの主な違いは以下の点です。
>
>■input_ids
>　・Transformerベースのモデルへ入力するための入力単語のIDです。
>　・単語に加えて、特別トークン(CLS、SEPなど)も含まれます。
>■word_ids
>　・単語列の各単語に対して割り当てられたIDです。
>　・特別トークンは含まれず、単語のみに対するIDです。

## word_idsの代わり
[HuggingFaceのチュートリアル](https://huggingface.co/learn/nlp-course/ja/chapter7/3?fw=tf#:~:text=%E3%81%AE%E5%89%8D%E5%87%A6%E7%90%86-,%E8%87%AA%E5%B7%B1%E5%9B%9E%E5%B8%B0%E8%A8%80%E8%AA%9E%E3%83%A2%E3%83%87%E3%83%AA%E3%83%B3%E3%82%B0%E3%81%A7%E3%82%82,-%E3%83%9E%E3%82%B9%E3%82%AF%E8%A8%80%E8%AA%9E%E3%83%A2%E3%83%87%E3%83%AA%E3%83%B3%E3%82%B0)

■HuggingFaceだとできないこと
　Tokenizerにis_fast要素がないとword_idsを作れない
<details><summary>チュートリアル(HuggingFace)のコード</summary>

```python
def tokenize_function(examples):
    result = tokenizer(examples["text"])
    if tokenizer.is_fast:
        result["word_ids"] = [result.word_ids(i) for i in range(len(result["input_ids"]))]
    return result


# Use batched=True to activate fast multithreading!
tokenized_datasets = imdb_dataset.map(
    tokenize_function, batched=True, remove_columns=["text", "label"]
)
tokenized_datasets
```
 </details>

■今回の主題
　Tokenizerにis_fast要素がなくてもword_idsを再現する方法の作成
```python

def tokenize_function(examples) :
    result = tokenizer(examples["text"])
    if tokenizer.is_fast :
        result["word_ids"] = [result.word_ids(i) for i in range(len(result["input_ids"]))]
    else :
        tokens = result["input_ids"]
        word_idsList = []
        for e, token in enumerate(tokens) :
            word_ids = []
            tmp_word_id = -1
            tmp_token = ""
            for word in token :
                word = tokenizer.decode(word)  
                if word.startswith("##"):
                    # 直前のトークンの連結
                    tmp_token += word[2:] 
                    word_ids.append(tmp_word_id)
                elif word == "[CLS]" or word == "[SEP]" :
                    word_ids.append(None)
                else :
                    tmp_word_id += 1
                    word_ids.append(tmp_word_id)
                    tmp_token = word
            word_idsList.append(word_ids)
        result["word_ids"] = word_idsList
    return result

# Use batched=True to activate fast multithreading!
tokenized_datasets = imdb_dataset.map(
    tokenize_function, batched=True, remove_columns=["text", "label"]
)
tokenized_datasets

```
単純に##で続いたらサブワードとして認識するようにした
```num_proc=4```の値を.map関数内にすると早くなるけど、エラーが出る...
```shell
Token indices sequence length is longer than the specified maximum sequence length for this model (759 > 512). Running this sequence through the model will result in indexing errors
```
## 時間の比較
mapが完了するまでにどのくらいの時間がかかったのか
<details><summary>環境</summary>

OS：Ubuntu 22.04.3 LTS (Jammy Jellyfish)
CPU：12 x Intel(R) Core(TM) i7-3930K CPU @ 3.20GHz (1 Socket)
GPU：なし
メモリ：20 GiB
データセット：imdb
</details>

■is_fast属性あり
01秒

■is_fast属性なし（自作した関数）
04分37秒
```shell:実行結果
Map: 100%|█████████████████████████████████████| 25000/25000 [01:09<00:00, 357.88 examples/s]
Map: 100%|█████████████████████████████████████| 25000/25000 [01:08<00:00, 363.72 examples/s]
Map: 100%|█████████████████████████████████████| 25000/25000 [02:20<00:00, 355.73 examples/s]
```
以下map中のモニタリング状況（実行中ほぼ不変）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3116042/10acc399-41b4-1ff3-f78f-322a125df5a2.png)
　⇒つまるところ全然CPUを使ってくれない
## 課題
とにかく遅い。