パスワードを忘れた? アカウント作成

yasuokaさんのトモダチの日記みんなの日記も見てね。 今週も投票をしましたか?

13767903 journal
Google

yasuokaの日記: Re: GoogleのBERTは漢文に返り点を打つことができるのか

日記 by yasuoka

私(安岡孝一)の一昨日昨日の日記の読者から、このツイートをお教えいただいた。ちょうど手元に工藤拓『形態素解析の理論と実装』(近代科学社、2018年9月)が届いて、ざっと読み始めたところだったのだけど、そっちはとりあえずおいといて、私個人としては、BERTの事前学習モデルに古典中国語(Classical Chinese)がほしい。ただ、現時点では「We do not plan to release more single-language models」らしいので、やるなら自分でやらねばならない、ということのようだ。

古典中国語のWikipediaは正直アテにならないので、事前学習の元ネタとしては、まあ、四書五経を中心とした古典群ということになる。kanripoで言えば、周易尚書毛詩禮記春秋左傳孟子論語あたりから始めて、適宜、テキストを拾ってくることになるだろう。ただ、古典中国語には、そもそもsentenceという単位はない(というかsentenceに切るということ自体がタスクの一つ)ので、処理単位として何が適当なのか、というあたりがまずは議論になる。これに対し、「章」を処理単位にする、というアイデアを、私個人としては持っている。たとえば、孟子卷一の最初の「章」は

孟子見梁惠王王曰叟不遠千里而來亦將有以利吾國乎孟子對曰王何必曰利亦有仁義而已矣王曰何以利吾國大夫曰何以利吾家士庶人曰何以利吾身上下交征利而國危矣萬乘之國弑其君者必千乘之家千乘之國弑其君者必百乘之家萬取千焉千取百焉不為不多矣苟為後義而先利不奪不饜未有仁而遺其親者也未有義而後其君者也王亦曰仁義而已矣何必曰利

すなわち152字しかないので、「章」を2つ並べても512 wordsに楽々おさまるはずなのだが、でも果たしてこれでいいのだろうか。また、WordPieceが手元にないので、代わりにSentencePieceを使いたいのだが、SentencePieceで言うwordは、漢字のまわりに空白を入れたWordPieceのwordと、同一視していいのだろうか。あるいは、BERTのtokenization.pyの

  def _tokenize_chinese_chars(self, text):
    """Adds whitespace around any CJK character."""

のあたりは、空白を入れずにSentencePieceで作ったvocab.txtの場合でも、このまま動かしておくべきなのだろうか。

このあたりが、どうにも私にはわからない上に、試しにいくつかの場合で実験しようとしても、私の手元の非力なマシンだと、いつまでたってもモデルが出来上がらない。BERTの事前学習モデルに古典中国語(Classical Chinese)がほしいのだけど、私自身はtwitterやってないし、さて、どうすればいいんだろ…。

13767059 journal
日記

yasuokaの日記: 漢文に返り点を打つタスク

日記 by yasuoka

昨日の日記で「漢文に返り点を打つタスク」と書いたところ、返り点にも流派のようなものがあるのではないか、という趣旨の御意見をいただいた。まあ、それもあって、今回、私(安岡孝一)の『漢文の依存文法解析と返り点の関係について』(日本漢字学会第一回研究大会(2018年12月1日)発表予定)では、『漢文大系』(冨山房、1909~1916年)の「孟子定本」とだけ対照している。卷一のアタマを見てみよう。

孟子見梁惠王
王曰
叟不千里而來
亦將以利吾國
孟子對曰
王何必曰
亦有仁義而已矣
王曰何以利吾國
大夫曰何以利吾家
士庶人曰何以利吾身
上下交征利而國危矣
萬乘之國
其君
必千乘之家
千乘之國
其君
必百乘之家
萬取千焉
千取百焉
多矣
苟爲義而先㆒㆑
奪不
仁而遺其親
義而後其君
王亦曰仁義而已矣
何必曰

上の例に即して言えば、「孟子見梁惠王王曰叟不遠千里而來亦…已矣何必曰利」という白文に対して、上のような返り点を導出するのが、漢文に返り点を打つタスクの目標ということになるだろう。ただ、確かに、上の例においても、安井衡の「返りグセ」みたいなものがあって、たとえば「曰」に返り点を打つかどうかは、かなり悩ましい。

上の例には「王曰」という文字列が2ヶ所あるが、最初の「王曰」には返り点が無く、後の「王曰」には直後に返り点がある。これは、最初の「王曰」が地の文にあり、後の「王曰」が孟子の会話文の中にあるからである。端的に言えば、地の文の「曰」には返り点を打たず、それに続く会話文の中の「曰」には返り点を打つことで、「曰」の入れ子構造を示しているわけである。正直かなり難しい。誰か、こういうタスクを、ちゃちゃっと処理してくれるようなモデル、作ってくれないかなぁ…。

13766537 journal
Google

yasuokaの日記: GoogleのBERTは漢文に返り点を打つことができるのか

日記 by yasuoka

『漢文の依存文法解析と返り点の関係について』(日本漢字学会第一回研究大会(2018年12月1日)発表予定)の発表準備をしていたところ、返り点程度ならGoogleのBERTを使えば打てるのではないか、という趣旨の御意見をいただいた。まあ、打てるかもしれないな、と思って、ここ5日間ほどBERT-Baseの2つのモデルと戯れてみたが、現時点での私(安岡孝一)の感触では、漢文の返り点はBERTの適用事例としては、かなり難しいのではないかと思える。

そもそもMultilingualモデルにもChineseモデルにも、古典中国語(Classical Chinese)が含まれておらず、いずれのvocab.txtもちょっとつらい。しかも不思議なことに、Chineseモデルのvocab.txtに「しにはとんとんワークケートを」なんていうvocabularyが含まれていて、どうなってるのかイマイチわからない。そこで、kanripoで公開中の大量の漢文に対して、まずは例の「穴埋め問題」を施すことを考えたのだが、BERTのTokenizationのところに

Because Chinese does not have whitespace characters, we add spaces around every character in the CJK Unicode range before applying WordPiece. This means that Chinese is effectively character-tokenized.

という妙なことが書かれていて、さすがに困り果てた。変な空白を入れてからWordPieceにかける、とか言われても、そのWordPieceどうすればいいの。代わりにSentencePieceを使う手も考えたが、それだとBERTのポリシーから離れていく気がする。さて、どうしたものか。

というわけで、現状のBERTでは、漢文に返り点を打つタスクをやらせるのは、ちょっとばかり無理がある。それとも私が見落としてるだけで、何か夢のような方法があるのかなぁ…。

13750689 journal
地球

yasuokaの日記: 古典中国語Universal Dependenciesにおける「S不X而Y」という構文 1

日記 by yasuoka

Universal Dependenciesで「孟子定本」を記述すべく頑張っていたところ、ふっと「S不X而Y」という形の構文が気になった。この構文は、「不」がXとYの両方を否定しているのか、あるいはXだけを否定しているかが、曖昧である。実際、「孔子不得中道而與之」では「得中道而與之」を否定していると考えられるが、「叟不遠千里而來」では「遠千里」しか否定していないようだ。

1 孔子 孔子 PROPN n,名詞,人,複合的人名 _ 3 nsubj _ SpaceAfter=No
2 不 不 ADV v,副詞,否定,無界 _ 3 advmod _ SpaceAfter=No
3 得 得 VERB v,動詞,行為,得失 _ 0 root _ SpaceAfter=No
4 中 中 NOUN n,名詞,固定物,関係 _ 5 nmod _ SpaceAfter=No
5 道 道 NOUN n,名詞,制度,儀礼 _ 3 obj _ SpaceAfter=No
6 而 而 CCONJ p,助詞,接続,並列 _ 7 cc _ SpaceAfter=No
7 與 與 VERB v,動詞,行為,得失 _ 3 conj _ SpaceAfter=No
8 之 之 PRON n,代名詞,人称,止格 _ 7 obj _ SpaceAfter=No

1 叟 叟 NOUN n,名詞,人,人 _ 3 nsubj _ SpaceAfter=No
2 不 不 ADV v,副詞,否定,無界 _ 3 advmod _ SpaceAfter=No
3 遠 遠 VERB v,動詞,描写,量 _ 0 root _ SpaceAfter=No
4 千 千 NUM n,数詞,数字,* _ 3 obj _ SpaceAfter=No
5 里 里 NOUN n,名詞,度量衡,* _ 4 clf _ SpaceAfter=No
6 而 而 CCONJ p,助詞,接続,並列 _ 7 cc _ SpaceAfter=No
7 來 來 VERB v,動詞,行為,移動 _ 3 conj _ SpaceAfter=No

しかしながら、これらはUniversal Dependenciesの依存構造では見分けがつかない。前者は「不←advmod─得─conj→與」であり、後者は「不←advmod─遠─conj→來」であって、構造上の差はないと考えられる。だとすると、これらを見分けるためには、Universal Dependenciesの次のステップ(あるいは、そのまた次のステップ)で何とかするしか無いのかしら?

13743864 journal
日記

yasuokaの日記: Z80におけるPaethの回転行列 1

日記 by yasuoka

一昨日昨日の日記を読み返しながら、イキオイ余ってPaethの回転行列を、Z80で汎用的に実装してしまった。IXレジスタにx座標の256倍を、IYレジスタにy座標の256倍を、HLレジスタに-tan(θ/2)の16384倍を、DEレジスタにsinθの16384倍を、それぞれ符号付整数で入れて呼び出すと、θだけ回転(ただし-2π/3≦θ≦2π/3)した結果がIX,IYレジスタに返ってくる。

06 03     LD B,3
DD E5   PUSH IX
FD E5   PUSH IY
E5      PUSH HL
CB 40    BIT 0,B
D9       EXX
C1       POP BC
D1       POP DE
E1       POP HL
20 01     JR NZ,+3
EB        EX DE,HL
CB 21    SLA C
CB 10     RL B
30 0C     JR NC,+14
AF       XOR A
93       SUB E
5F        LD E,A
9F       SBC A
92       SUB D
57        LD D,A
AF       XOR A
91       SUB C
4F        LD C,A
9F       SBC A
90       SUB B
47        LD B,A
CB 21    SLA C
CB 10     RL B
30 01     JR NC,+3
19       ADD HL,DE
CB 2A    SRA D
CB 1B     RR E
78        LD A,B
B1        OR C
20 F1     JR NZ,-13
E5      PUSH HL
D9       EXX
CB 40    BIT 0,B
20 03     JR NZ,+5
FD E1    POP IY
C2 DD E1  JP NZ,E1DDH
EB        EX DE,HL
10 C3   DJNZ -59
EB        EX DE,HL
C9       RET

θ=2π/48に対し、いくつかの-tan(θ/2)とsinθを試したところ、普通にHL=FBCEH, DE=085AHすなわち-tan(θ/2)=-1074/16384, sinθ=2138/16384とするのが、いちばん精度が良かった。この値だと、(100,0)すなわちIX=6400H, IY=0000Hから、48回まわしたところで、IX=640AH, IY=0000Hとなる。文句なしの精度だ。ただ、このプログラム、絶対にジャンプしない「JP NZ,E1DDH」とか、かなりアヤシイ部分もあるので、もし万が一どこかで使う場合には注意されたい。

 ┌ 1 -1074/16384 ┐┌     1      0 ┐┌ 1 -1074/16384 ┐
 └ 0      1      ┘└ 2138/16384 1 ┘└ 0      1      ┘

13743433 journal
日記

yasuokaの日記: Z80における2π/48の回転行列 2

日記 by yasuoka

昨日の日記を読み返しながら、そう言えば当時の私(安岡孝一)は、2π/48の回転行列をZ80で作りたかったが断念した、ということを思い出した。とっくの昔にあきらめた夢のはずだが、今の私ならどうするだろう。

sin(2π/48)を2進数表記すると0.00100001011010100010…、tan(π/48)を2進数表記すると0.00010000110001110111…、どちらもスジの良くないビットパターンなのだが、とりあえずsin(2π/48)≒67/512, tan(π/48)≒67/1024あたりで、Paethの回転アルゴリズムを使ってみよう。

 ┌ 1 -67/1024 ┐┌   1    0 ┐┌ 1 -67/1024 ┐
 └ 0    1     ┘└ 67/512 1 ┘└ 0    1     ┘

対象とするCPUがZ80なので、IXレジスタにx座標(上位8ビットが符号付整数、下位8ビットが256を分母とする分数)を、IYレジスタにy座標を入れることにする。

FD E5 PUSH IY
E1     POP HL
CB 2C  SRA H
CB 1D   RR L
7C      LD A,H
CB 2C  SRA H
CB 1D   RR L
84     ADD A,H
4F      LD C,A
9F     SBC A,A
47      LD B,A
CB 2C  SRA H
CB 1D   RR L
CB 2C  SRA H
CB 1D   RR L
09     ADD HL,BC
EB      EX DE,HL
DD E5 PUSH IX
E1     POP HL
B7      OR A
ED 52  SBC HL,DE
54      LD D,H
5D      LD E,L
7C      LD A,H
CB 2C  SRA H
CB 1D   RR L
84     ADD A,H
4F      LD C,A
9F     SBC A,A
47      LD B,A
CB 2C  SRA H
CB 1D   RR L
CB 2C  SRA H
CB 1D   RR L
09     ADD HL,BC
44      LD B,H
4D      LD C,L
FD 09  ADD IY,BC
FD E5 PUSH IY
E1     POP HL
CB 2C  SRA H
CB 1D   RR L
7C      LD A,H
CB 2C  SRA H
CB 1D   RR L
84     ADD A,H
4F      LD C,A
9F     SBC A,A
47      LD B,A
CB 2C  SRA H
CB 1D   RR L
CB 2C  SRA H
CB 1D   RR L
09     ADD HL,BC
EB      EX DE,HL
B7      OR A
ED 52  SBC HL,DE
E5    PUSH HL
DD E1  POP IX
C9     RET

(100,0)すなわちIX=6400H, IY=0000Hから始めると、このプログラムは48回よびだしたところでIX=6402H, IY=004EHとなった。x座標はほぼ元の位置に戻っているが、y座標の方は0.3ドットほど回りすぎだ。まあ、これでも十分な気がするが、これ、回転精度をもう少しだけ上げるには、どういう風にいじればいいのかな…。

13742732 journal
日記

yasuokaの日記: Z80における「手抜き」回転行列のさらなる改良 2

日記 by yasuoka

昨日の日記の読者から、Alan W. Paethの「A Fast Algorithm for General Raster Rotation」(Proceedings Graphics Interface '86 / Vision Interface '86 (May 1986), pp.77-81)という論文をお教えいただいた。以下の3つの三角行列の積で回転行列をシミュレートする、という優れモノで、かなり速い上に誤差が小さい。

 ┌ 1 a ┐┌ 1 0 ┐┌ 1 a ┐ _ ┌ cosθ -sinθ ┐
 └ 0 1 ┘└ b 1 ┘└ 0 1 ┘  ̄ └ sinθ  cosθ ┘

式を解くと、b=sinθ, a=(cosθ-1)/sinθ=-tan(θ/2)である。ここで、三角行列を使うメリットは、aとbが多少不正確な値でも、行列式が常に1になるという点にある。たとえば、cosθ≒127/128, sinθ≒1/8に対しては、a=-1/16, b=1/8すなわち

 ┌ 1 -1/16 ┐┌  1  0 ┐┌ 1 -1/16 ┐ _ ┌ 127/128 -255/2048 ┐
 └ 0   1   ┘└ 1/8 1 ┘└ 0   1   ┘  ̄ └   1/8    127/128  ┘

という「回転行列」を計算することになるので、少し歪んでいるものの、確かに行列式が1となっている。

このPaethのアルゴリズムを元に、私(安岡孝一)の一昨日のプログラムを改良してみよう。対象とするCPUがZ80なので、IXレジスタにx座標(上位8ビットが符号付整数、下位8ビットが256を分母とする分数)を、IYレジスタにy座標を入れることにする。

FD E5   PUSH IY
21 01 00  LD HL,1
39       ADD HL,SP
7E        LD A,(HL)
07      RLCA
9F       SBC A,A
ED 67    RRD
2B       DEC HL
ED 67    RRD
EB        EX DE,HL
C1       POP BC
DD E5   PUSH IX
E1       POP HL
B7        OR A
ED 42    SBC HL,BC
44        LD B,H
7D        LD A,L
CB 28    SRA B
1F       RRA
CB 28    SRA B
1F       RRA
CB 28    SRA B
1F       RRA
4F        LD C,A
FD 09    ADD IY,BC
FD E5   PUSH IY
EB        EX DE,HL
23       INC HL
7E        LD A,(HL)
07      RLCA
9F       SBC A,A
ED 67    RRD
2B       DEC HL
ED 67    RRD
EB        EX DE,HL
C1       POP BC
B7        OR A
ED 42    SBC HL,BC
E5      PUSH HL
DD E1    POP IX
C9       RET

(100,0)すなわちIX=6400H, IY=0000Hから始めると、このプログラムは65536回よびだした後でもIX=C3C9H, IY=B010Hで、0.08%も誤差がない。うーむ、ここまで凄いとは。でも、当時の私は、この論文を知らなかったというか、論文まだ発表されてなかったんだよなぁ…。

13741312 journal
日記

yasuokaの日記: Z80における「手抜き」回転行列の改良 9

日記 by yasuoka

一昨日昨日の日記を読み返していて、これ、今の私(安岡孝一)だったら、sinθ≒1/8じゃなくて、sinθ≒511/4096にしちゃうんだろうな、と思えてきた。その方がcosθ=127/128に近いからだ。

 ┌ cosθ -sinθ ┐  ┌ x ┐
 └ sinθ  cosθ ┘  └ y ┘

ただ、sinθ≒1/8に比べると、sinθ≒511/4096はインパクトが弱いし、何よりプログラムが長くなる。HLレジスタのHレジスタに符号付整数、Lレジスタに256を分母とする分数が入っているとすると、511/4096の乗算は

7C      LD A,H
2F     CPL
5F      LD E,A
CB 2B  SRA E
07    RLCA
9F     SBC A,A
57      LD D,A
19     ADD HL,DE
7D      LD A,L
CB 2C  SRA H
1F     RRA
CB 2C  SRA H
1F     RRA
CB 2C  SRA H
1F     RRA
6F      LD L,A

となってしまって、かなり長いし読みにくい。うーん、もうちょっと何とかならないかな…。

13740855 journal
日記

yasuokaの日記: Z80における「手抜き」回転行列の計算誤差

日記 by yasuoka

昨日の日記に対して、計算誤差はどうしたのか、という趣旨の御質問をいただいた。私(安岡孝一)の記憶が確かなら、何もやってなかった気がする。どのくらい誤差が出ていたのか、ちょっと確かめてみよう。

 ┌ 127/128  -1/8   ┐  ┌ x ┐
 └   1/8   127/128 ┘  └ y ┘

画面中心を(0,0)とするxy座標系において、cosθ=127/128, sinθ≒1/8の回転行列を用いて「手抜き」回転をおこなう。対象とするCPUがZ80なので、IXレジスタにx座標(上位8ビットが符号付整数、下位8ビットが256を分母とする分数)を、IYレジスタにy座標を入れることにすると、当時の私が組んだプログラムは、だいたい以下のような感じである。

DD E5 PUSH IX
FD E5 PUSH IY
E1     POP HL
7C      LD A,H
2F     CPL
57      LD D,A
7D      LD A,L
2F     CPL
CB 2A  SRA D
1F     RRA
CB 2A  SRA D
1F     RRA
CB 2A  SRA D
1F     RRA
5F      LD E,A
DD 19  ADD IX,DE
7C      LD A,H
2F     CPL
07    RLCA
5F      LD E,A
9F     SBC A,A
57      LD D,A
FD 19  ADD IY,DE
E1     POP HL
54      LD D,H
7D      LD A,L
CB 2A  SRA D
1F     RRA
CB 2A  SRA D
1F     RRA
CB 2A  SRA D
1F     RRA
5F      LD E,A
FD 19  ADD IY,DE
7C      LD A,H
2F     CPL
07    RLCA
5F      LD E,A
9F     SBC A,A
57      LD D,A
DD 19  ADD IX,DE
C9     RET

(100,0)すなわちIX=6400H, IY=0000Hから始めると、このプログラムは190回呼び出されたところで、IX=18EDH, IY=9E1EHすなわち(24.92578125,-97.8828125)となり、誤差が1%を超える。まあ、100ドットあたり1ドット外へズレてしまうわけだ。ただ、この「手抜き」回転行列は、50回程度でほぼ1周してしまうので、190回も呼び出される可能性は極めて低い。だとすると、整数部1バイトのxy座標系において、計算誤差は1ドット未満におさまるので、アクションゲームに使う分には大丈夫、というのが、当時の私の判断だったように思う。でも、現代のアクションゲームだったら、座標系を整数部1バイトで済ませる、ってことがそもそも有り得ないかしら。

13739653 journal
日記

yasuokaの日記: Z80における「手抜き」回転行列 4

日記 by yasuoka

ネットサーフィンしていたところ、このtweetで35年ほど前の悪行がバラされてしまっていたので、当時の記憶を辿りつつ、ざっと書いてみようと思う。

 ┌ cosθ -sinθ ┐  ┌ x ┐
 └ sinθ  cosθ ┘  └ y ┘

画面中心を(0,0)とするxy座標系において、適当な微小角度θで回転をおこないたい。θの値は、まあ何でも良くて、とにかく見た目が「回って」いる雰囲気を出したい。ただ、対象とするCPUがZ80なので、乗算は自分で実装するしかなく、そのあたりの「手抜き」を考える。

微小角度θにおいてcosθは1に近い値なので、ここの実装を簡単にすべく、とりあえずcosθ=1-2-kとしてみる。そうすると、sin2θ=2(1-k)-2-2kとなるので、小さい方の項を捨てれば、だいたいsinθ≒2(1-k)/2。つまり、kが奇数なら実装が簡単となり、たとえばk=7なら、cosθ=127/128, sinθ≒1/8となる。

HLレジスタのHレジスタに符号付整数、Lレジスタに256を分母とする分数が入っているとすると、1/8の乗算は単純に

7D      LD A,L
CB 2C  SRA H
1F     RRA
CB 2C  SRA H
1F     RRA
CB 2C  SRA H
1F     RRA
6F      LD L,A

で済む。一方、127/128の乗算は、私(安岡孝一)の記憶が正しければ

7C      LD A,H
2F     CPL
07    RLCA
5F      LD E,A
9F     SBC A,A
57      LD D,A
19     ADD HL,DE

と手を抜いた覚えがある。RLCAのところは、本来はLレジスタのトップビットを反転して持ってくるべきなのだが、それをAレジスタの符号ビットのコピーで誤魔化していて、少しだけ127/128からズレているわけだ。さて、今なら、どう書くかなぁ…。

typodupeerror

私は悩みをリストアップし始めたが、そのあまりの長さにいやけがさし、何も考えないことにした。-- Robert C. Pike

読み込み中...