3月18日の記事の続きだが、Qwen2Tokenizerのmergesを考えた場合、小書きの「ㇷ゚」をいきなり追加するのはスジが悪い。実際Qwen3-VL-2B-Instructで、「ㇷ゚」がどうトークナイズされるか、見てみよう。
>>> from transformers import AutoTokenizer
>>> tkz=AutoTokenizer.from_pretrained("Qwen/Qwen3-VL-2B-Instruct")
>>> print(tkz("ㇷ゚"))
{'input_ids': [124595, 115, 3402, 248], 'attention_mask': [1, 1, 1, 1]}
「ㇷ゚」は4つのトークンにバラされてしまう。中身はどうなっているだろう。
>>> print(ascii(tkz.convert_ids_to_tokens(tkz("ㇷ゚")["input_ids"])))
['\xe3\u0129', '\xb7', '\xe3\u0124', '\u013c']
つまり、「ㇷ゚」のUTF-8「e3 87 b7 e3 82 9a」が、「e3 87」「b7」「e3 82」「9a」の4つにバラされているということだ。これをうまく組み上げるには、まず「e3 87 b7」「e3 82 9a」を表す2つのトークン、すなわち「ㇷ」と半濁点(U+309A)に組み上げて、それをさらに「ㇷ゚」へと組み上げるよう、mergesを追加していくべきだということだ。Google Colaboratory (GPU版)だと、こんな感じ。
!pip install 'transformers>=5' accelerate jinja2
import json,torch
from transformers import pipeline,AutoTokenizer
nlp=pipeline("image-text-to-text","Qwen/Qwen3-VL-2B-Instruct",device=-1)
nlp.tokenizer.save_pretrained("tmpdir")
d=json.loads(nlp.tokenizer.backend_tokenizer.to_str())
e=nlp.model.get_input_embeddings()
for a,b in [("ㇷ","フ"),("\u309a","゜"),("ㇷ゚","プ"),("ㇰ","ク"),("ㇱ","シ"),("ㇺ","ム"),("ㇻ","ラ"),("ㇼ","リ"),("ㇽ","ル"),("ㇾ","レ"),("ㇿ","ロ")]:
x=nlp.tokenizer(a)["input_ids"]
if len(x)>1:
s=nlp.tokenizer.convert_ids_to_tokens(x)
z=s[0]
for c in s[1:]:
d["model"]["merges"].append([z,c])
z+=c
d["model"]["vocab"][z]=len(d["model"]["vocab"])
k=len(x)-1
for t in d["added_tokens"]:
t["id"]+=k
for c in [nlp.model.config,nlp.model.config.text_config,nlp.model.generation_config]:
for t in dir(c):
if t.endswith("_token_id"):
u=getattr(c,t)
if isinstance(u,list):
setattr(c,t,[i+k for i in u])
elif u:
setattr(c,t,u+k)
nlp.tokenizer.backend_tokenizer.from_str(json.dumps(d)).save("tmpdir/tokenizer.json")
nlp.tokenizer=AutoTokenizer.from_pretrained("tmpdir")
v=len(nlp.tokenizer)
if v>nlp.model.config.text_config.vocab_size:
e=nlp.model.resize_token_embeddings(v)
nlp.model.config.text_config.vocab_size=v
w=len(d["model"]["vocab"])-1
with torch.no_grad():
e.weight[w+1:v,:]=e.weight[w+1-k:v-k,:].clone()
else:
w=x[0]
x=nlp.tokenizer(["ャ","ヤ",b])["input_ids"]
with torch.no_grad():
e.weight[w,:]=e.weight[x[0][0],:]-e.weight[x[1][0],:]+e.weight[x[2][0],:]
nlp.modelcard=None
nlp.save_pretrained("tmpdir")
img="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/428317/55d10bae-10b0-4a18-8e27-0907bc16361f.jpeg"
nlp=pipeline("image-text-to-text","tmpdir",max_new_tokens=2048,device_map="auto")
d=nlp([{"role":"user","content":[{"type":"image","image":img},{"type":"text","text":"OCR Ainu sentences."}]}])
print(d[0]["generated_text"][1]["content"])
「アイヌの美―彩りと輝き―」(2026年1月31日~3月19日)のウエランカラㇷ゚を読ませてみたところ、私(安岡孝一)の手元では以下の結果が出力された。
ウエランカラㇷ゚
令和7年度アイヌ工芸品展「アイヌの美-彩りと輝き-」アニ アン レベ チㇿㇾ ワ、ウサㇺ ペカ イコㇿ チアマ ワ エチヌカレアン。
アイヌ アナッネ テエタ ワノ アン クㇽ ネ ワ 20 イㇰ ネ パ エㇺコ パッノヤウンモㇱㇼ、ヤンケモシリ(カラドト)、ㇽトㇺ オッタ オカイ。タネ ヤウンモㇱㇱッ タ カイサモㇿモㇱッ タカイ、モシリ エビッタ オカイ。テエタ アナッネ チㇰニ、ニカブ、キナカブ、チコイキナ ポネ、キㇻウ、シキテ、ㇽㇱ、チェㇷ゚カブ、セイ アニ ウサ オカイ ペ カㇻ。ネアンペ オッタ ケㇱト アネイワンケ ナ カイ アン。イノミ オッタ アネイワンケ ナ カイ アン。ピㇱカン モㇱリ エオカイ ウタト トゥㇻ、アイヌ ウイマㇺ ワ、センカキ、ヌイト、カネ、タマ コㇿ ワ ネアンペ アニ ピㇼカ イカラカラ ピㇼカ イスイエアイス キ。
タパン イキ オッタ イㇿホ ピㇼカ ナ、ヌベキ ピㇼカ ナ チスㇺケ ワ チサンケ。イコㇿ エチヌカレアン カトッ エネアニ。センカキ トゥㇻ アンデセ ワ アンカラ チタㇻペ カイ アン。ㇽトㇺ ワンㇰㇽ イカカラ ワ カㇻㇰ カイ アン。ヤンケモシリ ワンㇰㇽ コㇿ タマサイ カイアン ワ、ボン コンコ コㇿ タマサイ カイ アン。ウサ カネ ウサ エッキㇻウ アニ アントㇺテ イコㇿ、イカヨビコㇿ、タンパクオナ、マキリ カイ アン。イノミ オッタ アネイワンケ ポートゥㇰ、タカイサラ、パッチ カイ アン。
ネアンペ トゥㇻノ クスㇿッ タ シㇰス チカップ美惠子(1948~2010)カラ ペ カイ エチヌカレアン。エエパキタ、貝澤徹(イスイㇰㇽ)、下倉洋之(カネスイㇰㇽ)、藤戸康平(イコㇿカㇻㇰㇽ)タネ ウサ オカイ ピㇼカ プ カㇻ ワ コアスㇽㇱ ウタン ネ ワㇰスネアンペ カイ チサンケ ワ エチヌカレアン。
タ パ アナッネ、アイヌプリ ピラサ クニ、公益財団法人アイヌ民族文化財団(アスコㇿトミ アニ アイスプリ ピㇻサ ウタㇻ)ケㇱ パ キ プ ネ ワ、タネ 30 スイ チキ シㇻタンナ。タンペ エチヌカㇻ ワ、テエタ アイスプリ、タネ アン アイスプリ、アㇺキㇼㇰ インネチキ ピㇼカ。イヨッタ イオシ、タパン イキ チㇿㇿ テッサマ、ピㇼカイコロ ワネㇽサ ア ウタラウサ ウサ ウンカスイ ウタㇻ、パセタラ チㇿサイライケ シㇻタンナ。
北海道立鉄路芸術館
京都府
京都府京都文化博物館
公益財団法人アイヌ民族文化財団
ちょっとやり過ぎたか、という気がしないでもないが、これで小書きカタカナを安定して使えるようになった。さて、やり過ぎた部分を揺り戻すためにも、どのあたりを追加学習すべきかな。