2022/12/10

目玉を動かさずに視線を変える

ショウジョウバエ(Drosophila)は複眼の網膜を動かすことで、体や頭を動かさずに見るところを変えられるらしい。

Fenk, L.M., Avritzer, S.C., Weisman, J.L. et al. Muscles that move the retina augment compound eye vision in Drosophila. Nature 612, 116–122 (2022).
https://www.natureasia.com/ja-jp/nature/highlights/118350

網膜を動かす筋肉自体は1970年代に発見されていたが、今回、動きの追跡や固視微動を行っていることがわかったとのこと。

目玉は動かせないけどちょっとだけにしても視線を変えられるとは。

2022/08/19

ルートにマウントする暗号化LVM領域の拡張

目的


Debian 10や11をインストールするとき、「ディスク全体を使い、暗号化LVMをセットアップする」ことができます。 もし、このディスクがいっぱいになってしまうと、容量を増やしたくなります。

ここでは、ディスクを追加し、暗号化したうえで、ルートにマウントしたディスクの容量を増やしてみます。

失敗するとOSの起動ができなくなるので、元に戻せるようバックアップをとってから実行しましょう。実施する場合は自己責任で!

方法


構成


Physical volumeをPV1、Volume groupをVG1、Logical volumeが2つでLV-rootとLV-swapとし、構成が
[PV1]─→[VG1]┬→[LV-root]
              └→[LV-swap]
であるとき、これを
[PV1]┬→[VG1]┬→[LV-root]
[PV2]┘       └→[LV-swap]
のように変更し、増えた容量をLV-rootに割り当てます。 PV2が新しく追加したディスクをLVMのPhysical volumeにしたものになります。

ディスクの暗号化と起動時の暗号化解除のための手順


以下、root権限で実行します。

最初に追加したディスクを暗号化します。追加したディスクは/dev/sdcであるとします。

$ cryptsetup --cipher aes-cbc-essiv:sha256 --key-size 256 luksFormat /dev/sdc
実行するとパスフレーズの入力を要求されるので、設定したいパスフレーズを入力します。

なお、暗号化アルゴリズムとキーサイズの設定は参考文献のままですので、必要に応じて変更します。

さきほど設定したパスフレーズが正しく入力できたか確認するために、次のコマンドを実行します。

$ cryptsetup luksOpen /dev/sdc sdc_crypt
成功したら次のステップに進みます。
(省略してもよいかもしれませんが、もし暗号化を解除できない状態でこれ以降の作業を進めていくと今動かしているOSが起動しなくなります)

次に、暗号化ディスクのUUIDを確認します。

cryptsetup luksDump /dev/sdc
を実行すると、暗号化ディスクの情報が表示されます。
UUID:          	xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
のxxx部分がUUIDですので、この値を$(UUID)と書くことにします。

UUIDがわかったので、/etc/crypttabに次の一行を追加します。

sdc_crypt UUID=$(UUID) none luks,discard,initramfs
行の最後にinitramfsと書いておくのがポイントです。

編集を終えたら、

$ update-initramfs -u
を実行して、OSの起動時の処理内容を更新します。 これにより、追加したディスクのパスフレーズによる暗号化の解除処理をOSの起動時に実施できるようになります。

リブートして、起動時に暗号化を解除できるか確認します。

リブートすると、これまで通り、Debianインストール時に暗号化したディスクに対するパスフレーズの入力を求められ、 続いて、今回追加したディスクのパスフレーズの入力を求められます。

もし、パスフレーズの入力回数が増えていない(=今回追加したディスクのパスフレーズの入力が求められない)状態の場合、 なにか失敗しているので、これ以降のLVMの変更手順は実行してはいけません。OSが起動しなくなります。

今回追加したディスクのパスフレーズの入力を求められ、暗号化を解除したうえでOSが起動すると、 /dev/mapperにsdc_cryptが加わっているはずです。

LVMの構成変更


追加したディスクを暗号化し、それを解除できるようになったので、次はルートにマウントしているディスクの容量を増やします。

まず、Physical volumeにsdc_cryptを追加します。

pvcreate /dev/mapper/sdc_crypt
追加したPhysical volumeを既存のVolume group (ここではxxx-vg)に追加します。
$ vgextend xxx-vg /dev/mapper/sdc_crypt
追加できたか確認します。
$ vgdisplay -v
の最後のほうに、Volume groupを構成するPhysical volumeが表示されますので、それで確認ができます。

さらに、Total PE / Free PEの行を見ると、Free PEの値がわかるので、この値を覚えておきます。 ここではこの値を$(Free)とします。

Logical volumeのうち、rootのvolumeの容量を増やします。

$ lvextend -l +$(Free) /dev/xxx-vg/root
もし、$(Free)が1000なら
$ lvextend -l +1000 /dev/xxx-vg/root
とします。

最後に、ファイルシステムの容量を増やします。ext2またはext3、ext4であれば、

$ resize2fs /dev/xxx-vg/root
を実行することで、ルートのディスク容量が追加したディスクの容量分だけ増えます。 他のファイルシステムを使っている場合は、適切なコマンドを実行して容量を増やします。

これで容量が増えているはずなので、dfなどでディスク容量が増えていることを確認します。

容量が予定通り増えていれば、最後にリブートして、OSが問題なく起動するか確認します。

正常に起動すれば、作業は完了です。

参考文献


https://qiita.com/ynakayama/items/74096212b8177234be8a
https://blog.id774.net/entry/2014/05/13/481/

2022/06/05

Debian11の端末でのTERMとemacs

目的


Debian11をクリーンインストールした状態で、端末(gnome-terminal)の背景を黒にすると、emacsの文字が読みにくい状態になるので、視認性を上げてみます。

3つ方法を挙げますが、方法3が最も良い方法でしょう。

初期の状態


何もしていない手元のDebian11環境では、環境変数TERMが
TERM=xterm-256color
となっていました。

方法1


一時的にどうにかしたい場合の方法です。環境変数TERMに
TERM=screen-256color
を設定するだけです。この方法の欠点は、一部がイタリック体になってしまうことです。 また、lessにも影響があり、文字列検索時の強調表示がイタリック体になってしまいます。

方法2


次のコマンドを実行します。
$ mkdir -p ~/.terminfo/s/
$ cp /usr/lib/terminfo/x/xterm-256color ~/.terminfo/s/screen-256color
そして
TERM=screen-256color
を設定します。こうすると、設定内容はxterm-256colorのままで、名前だけscreen-256colorに変更したものが参照されるようになります。

emacsがTERMの名前でカラーテーマを決めているようで、端末の全体的な設定はもとのまま、emacsの色だけTERM=screen-256colorと同じにできます。

方法3


素直にemacsの設定を変更します。TERMの名前に依存してカラーテーマが決まらないように変更するだけです。

https://unix.stackexchange.com/questions/53467/emacs-colors-based-on-term-environment-variable によると、emacsを起動している状態で、

M-x customize-variable [ENTER]
frame-background-mode [ENTER]
を実行します。次のような画面が表示されるので、
For help using this buffer, see [Easy Customization] in the [Emacs manual].

                                         [ Search ]

Operate on all settings in this buffer:
[ Revert... ] [ Apply ] [ Apply and Save ]

Hide Frame Background Mode: [Value Menu] automatic
   [ State ]: SAVED and set.
   The brightness of the background. Hide
   Set this to the symbol ‘dark’ if your background color is dark,                                                    
   ‘light’ if your background is light, or nil (automatic by default)                                                 
   if you want Emacs to examine the brightness for you.                                                                 
                                                                                                                        
   If you change this without using customize, you should use                                                           
   ‘frame-set-background-mode’ to update existing frames;                                                             
   e.g. (mapc 'frame-set-background-mode (frame-list)).
Groups: [Faces]
[Value Menu]のところにカーソル(キャレット?)を合わせて、[ENTER]を押すと、
Available choices:

0 = dark
1 = light
2 = automatic
のように選択肢が表示されます。今回は背景が黒であるという前提ですので、0を押します。 その後、[ Apply and Save ] にカーソルを合わせて[ENTER]を押せば、設定が保存されます。

不思議なこと


infocmpでxterm-256colorの内容を見てみると、イタリック体にするためのエスケープシーケンスが
$ infocmp xterm-256color|egrep "sitm|ritm"
	rev=\E[7m, ri=\EM, rin=\E[%p1%dT, ritm=\E[23m, rmacs=\E(B,
	sgr0=\E(B\E[m, sitm=\E[3m, smacs=\E(0, smam=\E[?7h,
のようにsitmとritmに設定されているにも関わらず、対応するところでは文字の色が反転してしまいます。一方、screen-256colorでは
$ infocmp screen-256color|egrep "sitm|ritm"
のように項目が設定されていません。このscreen-256colorをTERMに使うとなぜかイタリック体になってしまいます。

さらに、https://gist.github.com/riobard/9166725を参考に、次の内容をscreen-256color-noitalic.terminfoとして保存し(ファイル名は任意)、

screen-256color-noitalic|screen with 256 colors with no italic,
	sitm=\E[7m, ritm=\E[27m,
	use=screen-256color,
次のコマンド
$ tic screen-256color-noitalic.terminfo
を実行すると、
~/.terminfo/s/screen-256color-noitalic
というファイルができ、TERMに設定できるようになるので、
TERM=screen-256color-noitalic emacs nanika-file
で実行してみても、上部に表示されるemacsのメニューの文字列がイタリック体のまま変化しません。lessでも同じです。
$ infocmp screen-256color screen-256color-noitalic
comparing screen-256color to screen-256color-noitalic.
    comparing booleans.
    comparing numbers.
    comparing strings.
	ritm: NULL, '\E[27m'.
	sitm: NULL, '\E[7m'.
のように差があるにも関わらずです。不思議ですね。どこかで何か設定があり、優先されているのでしょう。

なお、\E[3mがイタリックの開始、\E[23mがイタリックの終了、\E[7mが色反転の開始、\E[27mが色反転の終了です。 例えば、

echo -e "\E[3mitalic\E[23m \E[7mrev\E[27m"
を端末で実行すると、イタリック体や色反転で表示されることが確認できます。

2022/05/29

Self-attentionの計算方法と行列の形状

いまさらではあるものの、Self-attention (自己注意)の計算方法について途中の行列の形状に着目して調べてみました。

結果だけ知りたい方は、最後のまとめに進んでください。

参考文献


元論文はAttention Is All You Needです。

論文の行間を読むか参考文献を遡っていけば、具体的にどういう計算をするのか分かるのかもしれませんが、 論文の最後に、コードへのURL https://github.com/tensorflow/tensor2tensor が記載されていますので、こちらを主に参考にして、計算方法を調べていきます。

Q, K, Vはどうやって計算するの?


論文の式(1)で使われているQ=Query、K=Key、V=Valueの3つの値の計算方法を調べてみます。

論文のFig.1を見ると、入力が3つに分岐しているので、何かをどうにかして入力をQ, K, Vの3つにしていることはわかります。

まず、transformer_encode関数を見てみます。コメントによると、入力であるinputsの形状は(batch_size, input_length, 1, hidden_dim)とのことです。

関数の呼び出し直後に形状を変換していて、結局、inputsは(batch_size, input_length, hidden_dim)になっています。

その後、encoder_functionが呼び出されるのですが、これの中身は、 transformer_encoder です。この関数内の213行目から、common_attention.multihead_attentionを呼び出します。

キャッシュがなく、self-attentionの場合であれば、4650行目にて、compute_qkvが呼び出されます。 ここで、Q, K, Vが計算されているようです。

定義は

def compute_qkv(query_antecedent, ←これがcommon_kayers.layer_preprocess(x, hparams)
                memory_antecedent, ←これがNone
                total_key_depth,
                total_value_depth,
                ...
となっていて、memory_antecedentがNoneであることは、呼び出し元のtransformer_encoder:213に戻るとわかります。

memory_antecedentがNoneならquery_antecedentにしているので、self-attentionの場合、入力はquery_antecedentと考えればよさそうです。

さて、Q, K, V (コード中ではq, k, v)はcompute_attention_componentで計算されていて、入力は

antecedent: a Tensor with shape [batch, length, channels]
戻り値が
c : [batch, length, depth] tensor
となっています。filter_widthによって処理内容が異なるようですが、filter_width == 1のケースを見てみると、 4415行目
    return common_layers.dense(
        antecedent, total_depth, use_bias=False, name=name,
        layer_collection=layer_collection)
のように書かれています。バイアスなしなので、単にMatMulの計算をしているだけになります。

Tensorflowのドキュメントによると、

Dense implements the operation: output = activation(dot(input, kernel) + bias) <中略> kernel is a weights matrix created by the layer,
なので、
depth channels depth length [Output] = length [antecedent] × channels [W]
となります。ここで、[ ]は行列を表しています。[ ]の左が行数、[ ]の上が列数です。

以上を考慮してcompute_qkvを読むと、q, k, vの形状は

q = (batch, length_q, total_key_depth)
k = (batch, length_kv, total_key_depth)
v = (batch, length_kv, total_value_depth)
となっていることがわかります。

Self-attentionの計算はどうなるの?


multihead_attentionの引数に指定するattention_typeには色々種類があるようですが、デフォルト指定されているdot_productを見てみます。

dot_product_attentionの引数の説明には、

  Args:
    q: Tensor with shape [..., length_q, depth_k].
    k: Tensor with shape [..., length_kv, depth_k]. Leading dimensions must
      match with q.
    v: Tensor with shape [..., length_kv, depth_v] Leading dimensions must
      match with q.
と書かれています。

まず、1648行目で、

logits = tf.matmul(q, k, transpose_b=True)
と計算しています。論文中の式(1)の\(QK^T\)の部分です。

length_kv depth_k length_kv length_q [logits] = length_q [q] × depth_k [\({\rm k}^T\)]
のように計算されるので、logitsの形状は (..., length_q, length_kv) となります。

式(1)の\(\sqrt{d_k}\)で割る部分が見当たりませんが、どこかで計算されているとして、次にsoftmaxの計算をみてみます。これは、1654行目

weights = tf.nn.softmax(logits, name="attention_weights")
で計算されています。weightsの形状はlogitsと同じです。

ドロップアウトの処理をした後、1667行目

return tf.matmul(weights, v)
で、式(1)の計算が完了します。これは、
depth_v length_kv depth_v length_q [Attention] = length_q [weights] × length_kv [v]
を計算していますので、この関数の戻り値の形状は(..., length_q, depth_v)となります。

dot_product_attentionのコメント部分にも

 Returns:
    Tensor with shape [..., length_q, depth_v].
と書いてあります。

以上の計算の途中で得られる[weights]の形状が(..., length_q, length_kv)となっており、 self-attentionの場合はlength_q=length_kvですので、系列長の2乗で必要になるメモリや計算量が増えていくことになります。

まとめ


コードを調べたことで、論文の式(1)の各行列の形状は、

\(Q \in \mathbb{R}^{L_{\rm q} \times d_{\rm k}} \)
\(K \in \mathbb{R}^{L_{\rm kv} \times d_{\rm k}} \)
\(V \in \mathbb{R}^{L_{\rm kv} \times d_{\rm v}} \)
\(QK^T \in \mathbb{R}^{L_{\rm q} \times L_{\rm kv}} \)
\((QK^T)V \in \mathbb{R}^{L_{\rm q} \times d_{\rm v}} \)

であることが明確になりました。ここで、\(L_{\rm q}\)は\(Q\)の系列長、\(L_{\rm kv}\)は\(K\)と\(V\)の系列長です。 \(d_{\rm k}\)と\(d_{\rm v}\)は論文と同じです。

さらに、self-attentionの場合、\(L_{\rm q} = L_{\rm kv} \)ですので、単に\(L\)とすれば、

\(Q \in \mathbb{R}^{L \times d_{\rm k}} \)
\(K \in \mathbb{R}^{L \times d_{\rm k}} \)
\(V \in \mathbb{R}^{L \times d_{\rm v}} \)
\(QK^T \in \mathbb{R}^{L \times L} \)
\((QK^T)V \in \mathbb{R}^{L \times d_{\rm v}} \)

のように\(L\)の添字をなくせるのですっきりします。

\(Q\)と\(K\)と\(V\)は、入力\(X \in \mathbb{R}^{L \times C}\)をバイアスなしのDenseレイヤーに通すことで得ることができます。 つまり、Denseレイヤーの重み行列をそれぞれ \(W_{\rm q} \in \mathbb{R}^{C \times d_{\rm k}} \)、 \(W_{\rm k} \in \mathbb{R}^{C \times d_{\rm k}} \)、 \(W_{\rm v} \in \mathbb{R}^{C \times d_{\rm v}} \)とすると、

\(Q = X W_{\rm q}\)
\(K = X W_{\rm k}\)
\(V = X W_{\rm v}\)

となります。ここで、\(C\)は、\(X=\{x_1, x_2, ... ,x_i ,... x_L\}\)としたときの特徴ベクトル\(x_i\)の次元数です。

2022/05/21

Raspberry Pi OSをdocker内で動かす

Intel CPUの64bit環境下で、Dockerを利用してRaspberry Piの32bit OS環境を動かしてみます。

Dockerの準備


Debian11環境で作業します。

Dockerがインストールされていなければ、例えば https://docs.docker.com/engine/install/debian/ を参考にDockerをインストールします。

イメージの準備


最初に、https://downloads.raspberrypi.org/から必要なRaspberry Pi OSのroot.tar.xzをダウンロードします。

Raspberry Pi OS Lite (32-bit)のバージョンbullseyeであれば、 https://downloads.raspberrypi.org/raspios_lite_armhf/archive/2022-04-07-11:57/ にあります。

Dockerのイメージへの変換


次に、ダウンロードしたroot.tar.xzをDockerのイメージへと変換します。例えば、
$ docker image import ./root.tar.xz pi11-32bit:2022-04-07
とします。インポートできたかは、
$ docker images
REPOSITORY    TAG          IMAGE ID       CREATED          SIZE
pi11-32bit    2022-04-07   d2b599f3a584   23 seconds ago   1.16GB
のようにして確認できます。

この時点では、まだ実行できません。

$ docker run -it pi11-32bit:2022-04-07 bash
exec /usr/bin/bash: exec format error

(以下、方法2を実施し、その後、リブートした後に方法1を実行しましたが、もしかすると方法2の影響が残っている可能性があります)

方法1


パッケージqemu-user-staticをホストPCにインストールするだけです。 インストールすると、
$ docker run -it --rm pi11-32bit:2022-04-07 uname -m
armv7l
のようにエラーが起きずに実行できるようになります。

方法2


$ docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
を実行します。イメージがダウンロードされて、その後、実行されます。

実行が完了すると、ホストPC側に/proc/sys/fs/binfmt_misc/qemu-*が作成され、

$ docker run -it pi11-32bit:2022-04-07 bash -c ls
bin  boot  dev	etc  home  lib	lost+found  media  mnt	opt  proc  root  run  sbin  srv  sys  tmp  usr	var
のように動くようになります。

なお、ホストPCをリブートすると、ホストPCで

$ ls /proc/sys/fs/binfmt_misc/
python3.9  register  status
となり、qemu-で始まるファイルがなくなります。また、
$ docker run -it pi11-32bit:2022-04-07 bash
exec /usr/bin/bash: exec format error
となり、実行できない状態になります。

方法2の仕組み


multiarch/qemu-user-staticコンテナ内で、ホストのbinfmt_miscとqemu-user-staticを設定することで動作させているようです。

まず、 https://github.com/multiarch/qemu-user-static/blob/master/containers/latest/register.sh を見ると

mount binfmt_misc -t binfmt_misc /proc/sys/fs/binfmt_misc
と書かれており、カーネルモジュール binfmt_misc をマウントしています(lsmodを実行するとbinfmt_miscが存在することが確認できます)。

少なくともここでホストのルート権限が必要になり、--previlegedをつけることになります。

また、register.shの最後に呼び出されているファイルはおそらく https://github.com/qemu/qemu/blob/master/scripts/qemu-binfmt-conf.sh で、この中のqemu_register_interpreter()にて

qemu_generate_register > /proc/sys/fs/binfmt_misc/register
という記述があることから、最終的にbinfmt_miscに対して自動的にqemuを動作させるよう登録をしています。

参考


https://www.koatech.info/blog/raspbian-on-docker/
https://qiita.com/autch/items/c8c9cdc7b8e5821e81a4
https://github.com/multiarch/qemu-user-static
https://github.com/qemu/qemu/blob/master/scripts/qemu-binfmt-conf.sh
https://wiki.bit-hive.com/north/pg/binfmt_misc
https://qiita.com/yuyakato/items/5dd06fb179922206044d

2022/02/20

gir

gir は GObjectIntrospection Repository の略とのこと。

DebianやUbuntuのパッケージの中に先頭に gir1.2- が付くパッケージが多数あり、このgirとは何だろうということで調べてみたところ、
https://mail.gnome.org/archives/gnome-shell-list/2011-April/msg00101.html
に説明を見つけました。

Introspectionの説明は
https://developer.gnome.org/documentation/guidelines/programming/introspection.html
にあります。C言語のコードからAPI情報を抽出して、C言語以外の言語から呼び出せるようにする仕組みのようです。

2022/02/15

Firefox 97でペンを使う

Windows10環境でFirefox 97にアップデートしてペンでタブを移動させられなくなってしまった場合は、about:configから

dom.w3c_pointer_events.dispatch_by_pointer_messages
をfalseに設定しましょう。これで、ペンでタブを移動させられるようになります。

タッチ操作でできる画面のスクロールがペンではできなくなりますが、マウスと同様にテキストを選択できるようになるので、マウスの代わりに利用したい方にはこちらのほうが都合が良いでしょう(そもそも以前はこちらがデフォルトの動作でした)。

2022/02/08

WiMAX+5Gを4Gのみで使う

1行まとめ


WiMAX+5GをSpeed Wi-Fi 5G X11で利用されていて、頻発する4Gと5Gの切り替えに難儀されている方は、回線がまともになるまでは「エコ」モードを使用しましょう。

発生している現象


WiMAX+5Gを利用してみました。速くていいですね。

しかしながら、利用場所によっては落とし穴があります。 中途半端に5Gが受信できる場所では、5Gと4Gが頻繁に入れ替わる現象が発生します。

これが理由なのかどうか明確にはわからないのですが、通信が引っかかる現象が頻発します。 ブラウザで検索したりどこかのウェブページを表示したり、ゲームで通信が発生したときなど、 全く反応がなく、結局接続できず失敗することが起きます。おそらくタイムアウトしているのでしょう。

不思議なことに、その現象が発生しているときに別の端末で別のウェブページを開くことは普通にでき、 pingにも影響がみられませんでした。

想像ではありますが、通信そのものは常にできているけれど、ときおりパケットが行方不明になってしまっていて、 行方不明になったパケットに依存している処理がずっと止まっているのかもしれません。

切り替え周期を測定してみたところ、おおむね25秒〜35秒で4Gから5Gへ、または、5Gから4Gへの切り替えが発生していました。 だいたい1分でもとに戻ってくるということですね。

アンテナが2本しか立たないところで使ってみると、4Gのみになり、切り替えは発生しないものの、 ダウンロードはともかくアップロード速度が100kbps未満というほぼ使えない状態になりました。

他に事例は?


こちらの方 https://hudetoro.com/life/2019/ が同じような現象にみまわれているようです。記事を読む限り、通信を提供している側ですぐになんらかの対処が実施されることはなさそうです。

対処方法


通信が切れる問題の原因が仮に4Gと5Gとの切り替えにあるとすると、切り替えが頻発しないようにすれば安定すると思われます。 そもそも5Gを利用しなければ問題ないだろうということです。

調べたところAndroidであれば5Gを使わない設定ができるようです。WiMAX+5Gの端末はAndroidではないので同じ方法は使えそうにありません。

そこで、X11の取扱説明書を読んでみたところ、なんとp.64に

省電力モードが「エコ」モードを選択した場合、WAN 側回線「5G」は無効となります。
と記載がありました(最初の「が」は「で」の誤植っぽいですが、原文ママです)。

というわけですので、通信が切れて困っている方、X11であれば「エコ」モードにすればもしかしたら問題がましになるかもしれません。