情報系の手考ノート

数学とか情報系の技術とか調べたり勉強したりしてメモしていきます.

次元を意識した数式の見方

物理現象に関連する数式では各変数に長さや重さ等が対応しています. これらをうまく使えば式変形等の正当性の確認や,逆に始めて見た式の理解の助けに役立ったりします. ここで長さや重さ等は次元と呼ばれており,この次元を意識して数式を読む方法についてこの記事で述べていきます.

前置き

物理現象に関する式の変数には次元というものがあります. ここで言う次元とは長さや重さ,時間や速さ等,いわゆる単位のことです.

例えば長さ a\; [\mathrm{m}],半径 r\; [\mathrm{m}]の円柱があり,この円柱の重さが m\; [\mathrm{k g}]だとします. このときこの円柱の体積は r^2 \pi a\; [\mathrm{m}^3]になります. また密度は \frac{m}{r^2 \pi a}\; [\mathrm{k g} \mathrm{m}^{-3}]となります. ここで体積に注目すると,長さに相当する rの2乗と長さに相当する aの積になっています. つまり長さを3回乗じているわけです. これに体積の単位を見ると,同じように長さであるメートルの3乗となっています. 同じように密度の単位の重さを体積で割ったものになっています. 乗算や除算において単位はそのまま影響を受けていることになります.

他の場合でも同じようなことが成立します. 例えば a\; [\mathrm{m}]を t\; [\mathrm{s}]で走った場合の速度は \frac{a}{t}\; [\mathrm{m} \mathrm{s}^{-1}]であり,やはり除算と対応した単位になっています.

では足し算や引き算についても考えてみましょう. 足し算の場合として, a\; [\mathrm{m}]と b\; [\mathrm{m}]を足す場合はどうでしょう. この場合 a bの和は長さになる,つまり a + b\; [\mathrm{m}]と見るのが自然でしょう. 引き算も同様に,同じ単位同士の和や差では単位が変わらないと見るのが自然と思われます.

では異なる単位同士ではどうなるでしょう.  a\; [\mathrm{m}]と b\; [\mathrm{k g}]の和を考えましょう. これは長さと重さを足しているわけですが,この和の単位を考えることに意味が無いように思います. 実際物理の式においてこういった,異なる単位を持つ値同士の和や差には意味がありません. 等号や不等号でも同様で,異なる単位を持つ値同士の比較は意味を成しません. まぁ 3\;[\mathrm{k g}]と 3\; [\mathrm{m}]が等しい!とか言われてもだから何?としかならないので,直感的に妥当と言える話でしょう.

次元

ここまで単位として"メートル","キログラム","秒"を用いて数式中に現れる単位と四則演算の関係について見てきました. これらはそれぞれ"長さ","重さ","時間"の単位であり,同様の単位では"尺","ポンド","分"等異なるものもあります. ですが"長さ","重さ","時間"をそれぞれ表しているというのは同じです. この"長さ","重さ","時間"は次元と呼ばれていて,例えばある棒の長さが a\; [\mathrm{m}]であると言った場合には, aは"長さの次元を持つ"みたいなことを言ったりします. また円周率や比,角度等は次元を持たず"無次元量"と呼ばれます.

式中の各変数や定数に次元が与えられれば,式全体にも次元が定まります. 和や差は同じ次元を持つもの同士でなければ意味を成さず,積や商で次元が変化します. また例外的に無次元量と積をとっても元の変数の次元は変化しません. 長さを何倍しても長さのままと言えば意味は伝わると思います.

このことから式全体の次元がわかっていれば,特定の変数の次元を推測することができます. 例えば


  m \frac{d^2 \theta}{d t^2} = - \frac{m}{l} g \sin \theta

という式があり, \thetaが無次元量, mの単位が [\mathrm{k g}], lの単位が [\mathrm{m}], tの単位が [\mathrm{s}]だとします. このとき gの単位を推測してみましょう. ここで微分が出てきていますが,割り算と同じように考えて問題がありません. すると左辺の単位は [\mathrm{k g} \mathrm{s}^{-2}]になります. 異なる単位同士を等号で結ぶことに意味が無いという点から,右辺も同じ単位になっているべきでしょう.  \sin \thetaは無次元なので単位として考えるなら無視して良さそうです. となると考えるべきは \frac{m}{l}の単位で,これは [\mathrm{k g} \mathrm{m}^{-1}]となります. ここから逆算すると gの単位は [\mathrm{m} \mathrm{s}^{-2}]となります. これは加速度の単位と対応します. 実際上の式は重力加速度を gとした単振り子の運動方程式です. このように不明な変数があったときに,その変数の意味を推測することが可能になります.

今の例では SI 単位をベースに使いましたが,他の単位でも同じことができます. 電位や電流を使った式として


  \tau \frac{d V(t)}{d t} = - (V(t) - V_\mathrm{rest}) + R I(t)

という式を考えます. ここで tを時間, V(t)は時刻 tにおける電位, V_\mathrm{rest}を電位, Rを抵抗, I(t)を時刻 tにおける電流とします. このときオームの法則において抵抗と電流の積が電位になることを思い出すと \tauが時間の逆数の次元を持つことがわかります. ここで時間の単位に秒を採用するなら, \tauの単位は [\mathrm{s}^{-1}]となるということです. 実際にこの式は Leaky Integrate-and-Fire モデルと呼ばれる,ニューロンの膜電位のモデルで, \tauは時定数となり次元が一致します.

このように次元を意識することで知らない式の謎の変数が何者がを考察することができるようになります. 次元のメリットはこれだけでなく,自分で式変形した式の正当性の評価にも使うことができます. これは単純で,変形した式と変形前の式の次元が一致するかを確認するだけです. もし一致しなければ何か変なことをしでかしたことになります(一致したから正しいとはなりません).

まとめ?

以上で述べたように次元を意識すると式変形や知らない式を理解するときに役立ったりします. ただ次元を持った 1が省略されたりしていて困るときがありますが,式変形等では大いに役立つので,機会があれば気にしてみることを推奨します.

TeX のすゝめ

研究だったりなんだったりで tex を使うことがあると思います. 今までほとんど tex を使わなかったのに tex を使うとなると,いろいろとわからないことがあって大変だと思います. 中には普段のノートに tex を使っちゃうなんていう危篤な方もいらっしゃるでしょう(私のように).

そんな tex 初心者や危篤な方に向けて,私が持つ用語(?)や知見等をここに並べておきます. あくまで列挙することを目的とし,簡単に概要の述べるのみとします. 適宜興味があったりするものについて調べ,自分で判断し利用して下さい. 初心者はきっと多くが役に立つと思いますし,危篤な方でも案外知らないことがあるかもしれません. また tex がかなり歴史の長いシステムである関係上,ネット上に古い情報が多数存在します. 現在は解消されている問題に対して,適切でない方法で解決策を与えている場合があったりするので注意が必要です. この記事は 2023 年 3 月現在でそこまで古くない情報のはずなので,そういった意味でも古い知識の更新のためにもある程度役に立つと思われます. 気楽にご拝読いただければ幸いでございます.

プリアンブル

tex で文書を書く場合,だいたいこんな構成の tex ファイルがテンプレートになるでしょう.

\documentclass{jlreq}

\usepackage{amsmath}% プリアンブル

\begin{document}
hgoe% 本文
\end{document}

このうち\begin{document}より前の部分がプリアンブルと呼ばれます. パッケージの読み込みや各種設定は原則としてプリアンブルにて行われます. そして\begin{document}\end{document}の間に本文を書くことになります.

jlreq

tex を使う場合,よほどのことがなければドキュメントクラスというのを設定します. tex ファイル冒頭で\documentclass{アレコレ}と入力するアレです. 良くネットで見るのは jsarticle だったり jsbook とかですね. jlreq はそのドキュメントクラスの1つで,最近の日本語 tex 環境では選んで損が無いようなものになっています. jlreq は日本語組版処理の要件を満たすように作成されたもので,とりあえず読み込んでおけばきれいな日本語文書が出来上がります. 加えて jsarticle 等と比べてかなり新しいドキュメントクラスであるため,設計が比較的モダンであるというのが強いです. 余白の設定等もやってくれるので既存のドキュメントクラスよりも設定が楽になっていて有り難いです. 詳しい使い方は jlreq のドキュメントを読んで下さい. 日本語文書作成者向けのものなので当然日本語で書かれているので英語を怖がる必要はありません.

ただし気をつけないといけないことがあります. 既存の jsarticles 等を使っている人は特に注意すべきなんですが,graphcx パッケージを読み込むと jlreq パッケージで設定した余白関連の設定が上書きされちゃいます. なので jlreq を使う場合には graphcx パッケージは読み込まない方がいいかもしれないです.

Biblatex

tex を用いると図表の番号の引用が楽であることは良く述べられます. tex の実力はそんなものではありません. 参考文献の引用だって楽になるのです! 少し詳しい人ならば以下のように書けばいいんだろ,となるかもしれません.

\begin{thebibliography{99}
\bibitem{hoge} あいうえお
\bibitem{fuga} いろはにほへと
\end{thebibliography}

この方法であれば\cite{hoge}とすることで自動的に適切な文献番号を参照してくれ,文章の適当な箇所に参考文献の一覧を載せることができます. しかし文献を載せるフォーマット等は自分でうまいことやる必要があり,また文献の並び順も手動で整理する必要があります.

これをもっと楽にできるのが Biblatex です. よくネットで適当に調べると bibtex が出てくるのですが,Biblatex の方が圧倒的に便利なので bibtex を使う明確なメリットは少ないと思います. 強いてあげるなら情報が多いことでしょうか. Biblatex は bib ファイルという文献情報が記載されたファイルを読み込んで,自動的に参考文献を並べてくれます. フォーマットもいくつか選ぶことができ,また少しフォーマットを変えたい場合はマクロで設定することができます. 多くの論文誌等が bib ファイルを公開しているため,わざわざ論文の情報を全て手入力する必要もなく論文作成が捗ります. また bib ファイルさえ用意すればフォーマットは後で自由に変えられるため,bib ファイルを使い回すことも可能です. 読んだ論文の bib ファイルを集めておけば,論文作成時に参考文献の章作成が大幅に楽になることでしょう.

amsmath 系

tex を使う理由の多くに数式が簡単に書けるだの,数式がきれいだのというものがあります. この数式を利用する上で amsmath 系のパッケージは実質必須になっています. いろんなマクロ等が定義されているため,amsmath 系のパッケージ無しでは tex の本領は発揮できないとすら言えます.

とりあえず以下のパッケージを読んでおけばいいでしょう.

  • amsmath
  • amsxtra
  • mathtools

また数式を書く場合には慣例が多くあるので,適宜調べておくと良いと思います. 例えばベクトルは太字で書くといったものがあります. その手の慣例はその分野の人間にとって常識となっているため,慣例に従った数式であれば誤解を生みにくくなります. また慣例を知っておくことで,論文を読む際に数式が飛躍的に読みやすくなるため適当にネットで調べて眺めておくと良いと思います.

研究でも Git は使える

git というソフトウェアがあります. 昨今のソフトウェア開発で多用されるヤツです. branch をわけて,commit して push だ merge だとかいろいろやって使うというアレです. そんな git ですが研究でも十分使えます. 実際私は修論や,論文読解やゼミ用のノート,実験用のプログラム等は git で管理していました. ということで研究で git はどう使えばいいの?ということに関して,書いていきたいと思います.

何を管理するの?

研究をしているといろんなファイル等を管理することになります. 私は以下のファイル群を git で管理していました.

  • 読んだ論文をまとめたノート
  • 学習内容や提案手法をまとめたノート
  • 実験用のプログラムとスクリプト
  • 修論(卒論も)

これらは適宜加筆修正が発生するため git でバージョン管理をしておくと安心できました. 特に実験用のプログラムとスクリプトは実験ごとにバージョンを付けておけるため,再現性の確保にも繋ります.

どうやって管理した?

ソフトウェア開発で git を使おうとすると,branch をどうわけるだのといろいろな情報が出てきます. 私はソフトウェア開発なんて手を出したことが無く,git の良い使い方なんて知らないですが,それでも十分便利だったと感じています.

管理の方法としてはただ適当なタイミングで commit するというだけの非常にシンプルなものです. commit メッセージはどんな修正をしたかというだけのものです. また適宜 gitlab のプライベートリポジトリにプッシュしていました. 恐らく git の使い方としては最低限のものでしょう.

特にプログラムに関しては実験ごとに commit メッセージに実験をしたという内容を書くようにしていました. これによって好きなタイミングの実験を確実に再現できるようになります.

また頻繁に push しておけば常にリモートリポジトリにバックアップがある状態で研究が進むので,最悪環境がおじゃんになっても復旧が容易であるというメリットがあります. まぁこれはかなりわかりやすいものではありますが.

このメリットとは別のメリットとして,commit の履歴を見ることでいつ何をしていたかがわかるというものがあります. つまり研究日誌として git のリポジトリが利用可能になるということです. 実験等をしたら当然考察を行わなければなりません. 他にもプログラムの動作が想定外なら,原因を考え解消する必要があります. つまり,いろいろ考えて,それに応じて行動するということを繰り返すことになります. この考えた内容はどこかに保存していないと,当時何を考えていたか忘れてしまいます. 数週間前ならまだ思いだせるかもしれませんが,数ヶ月前ならほぼ不可能でしょう. そんな時,実験用のプログラムを修正した commit メッセージになんでそうしたかが書いてあれば,どういう考えでその修正を行ったのかがわかるわけです. まぁ普通に日誌をつけてもいいんですが,git をつかえばバックアップをとりながら日誌も書けるという点で効率が良いです.

それに commit には日時の情報もあるので,いつどれだけの時間研究していたかもわかります. これは自分が研究をちゃんとやっているという保証にも繋がるので,教員に救ってもらえる可能性が高まります.

まとめ

git を使ってソフトウェアを開発したりするわけですが,そんな開発にはいろいろとマナーみたいなものがあります. そんなもん気にしなくても git は使えるので軽率に使ってください. というか使って始めてわかることとかもあるので,最初からちゃんとやろうとしなくていいです. 研究でなら尚更です. バックアップをとりながら,各バージョンにメモを残しておける程度で使っても十分役に立つので,気軽に使いましょう. それが結果的に git の学習にもつながります.

GPUメモリ不足時超初歩的トラブルシューティング:PyTorch

2023年現在,PyTorch や TensorFlow があるおかげで,かなり簡単にニューラルネットワーク関連のプログラムが作れるようになっています. 特に PyTorch はカスタマイズ性が高く,研究用途等で非常に有用です. しかし研究で使うぞとなっても,なんとなくの仕組みも知らない状態で使うとメモリ効率が非常に悪いプログラムになったりします. そんな効率が悪いプログラムは,学部や修士の研究において「とりあえず実装しよう」というような思考になっても実装されたりするでしょう.

実際,私も PyTorch で書いた効率の悪いプログラムで研究を進めていた1人でした. しかし実験の過程でメモリ不足となったことでプログラムの効率化を図る機会がありました. その際に行った修正は非常に初歩的なものではあるものの,意識しておかないと簡単にメモリ不足へと陥いる要因となります. そこで当時私が確認した内容を記すことで,後学の徒へ向けた記事とします.

事前知識

最低限 PyTorch について知っているべき(?)内容についてここで述べます.

PyTorch は機械学習(特にニューラルネットワーク)の実装によく用いられている Python のライブラリです. 特に研究用途で用いられることが多いように感じます(偏見). 機械学習を実装する上で頻繁に扱うのが多次元配列と勾配の計算です. PyTorch ではテンソルという名前で多次元配列を扱っており,基本的にはテンソル同士の演算によって機械学習のモデルを実装します. また近年よく話題になる人工知能であるニューラルネットワークは,勾配を計算することで学習を行います. PyTorch では勾配を計算するための仕組みとして計算グラフというものを用いています. 特に気をつけずに実装していた場合,GPU 上のメモリが不足する理由の上位を独占しているのがこの計算グラフでしょう. 私もこれが原因でした. 以下で示すのは,いかにして不要な計算グラフを保持しないようにするか,という内容になります.

超初歩的トラブルシューティング

まずは計算グラフを切る方法です. 計算グラフはただ値を計算するのに加えて,どうやって値が計算されたかも保持しています. その分余計にメモリを必要とするわけです. となれば使わない計算グラフは持たない方がメモリを無駄にすることはなくなります. これは計算グラフを切るという操作によって実現されます. 下のソースコードにおいて detach_flag の有無を切り替えて実行すると,実際にメモリ使用量に差が出ます.

import torch
from torchvision.models import alexnet

def print_allocated_memory():
    print("{:.2f} GB".format(torch.cuda.memory_allocated() / 1024 ** 3))

detach_flag = True

model = alexnet().cuda()

x = torch.rand(1024, 3, 224, 224, requires_grad=False).cuda()
y = model(x)
if detach_flag:
    y = y.detach()
print_allocated_memory()

私の環境では detach_flag が真のときは 0.85 GB,偽のときは 3.76 GB となりました. どれだけ省メモリ化できるかはモデルによって変わりますが,実際に detach によって省メモリ化できていることは確認できたかと思います. ただし,detach した y からは勾配を計算してモデルのパラメータ更新はできなくなります. なので勾配がいらない値は適宜 detach することで無駄にメモリを食うようなことは避けられるでしょう.

また下記プログラムでも detach と同様の効果があります.

import torch
from torchvision.models import alexnet

def print_allocated_memory():
    print("{:.2f} GB".format(torch.cuda.memory_allocated() / 1024 ** 3))

model = alexnet().cuda()

x = torch.rand(1024, 3, 224, 224, requires_grad=False).cuda()
with torch.no_grad():
    y = model(x)
print_allocated_memory()

no_grad を使う場合は計算全体で計算グラフが構築されなくなります. そのため学習ではなくテストデータの評価時に使うことが多くなるでしょう. 対して detach では計算グラフ自体は計算されます. 私は学習中に計算した損失関数の値を Tensor でとっておきたいときに多用しました. 学習の過程で損失関数の値を累積してとっておきたい,というような状況で下のプログラムの y_sum のように累積させていくことはあると思います. このような状況で detach は効果を発揮します. まぁ損失関数の累積なら Tensor である必要が無いんですけどね.

下のプログラムで detach_flag が真のときは 0.81 GB,偽のときはメモリ不足となりました(GPU のメモリは 8GB). detach しない場合は,10 回のループ全てにおいて計算グラフを保持する必要があるため膨大なメモリが必要になります. しかし detach しておけば過去の計算グラフは不要になるため,1回の計算分のみ計算グラフを保持しておけば良くなります. このように学習中に Tensor の値を累積させて保持したい場合には detach を使うことでメモリを効率的に使えます.

import torch
from torchvision.models import alexnet

def print_allocated_memory():
    print("{:.2f} GB".format(torch.cuda.memory_allocated() / 1024 ** 3))

detach_flag = True

model = alexnet().cuda()

y_sum = 0
for i in range(10):
    x = torch.rand(1024, 3, 224, 224, requires_grad=False).cuda()
    y = model(x)
    if detach_flag:
        y_sum = (y_sum + y).detach()
    else:
        y_sum = y_sum + y
    del y
print_allocated_memory()

ちなみにプログラム中の del は python のスコープの関係でローカル変数 y を破棄するために使っています. このような変数のスコープを意識するのも無駄なメモリの使用を避ける方法の1つです.

まとめ

以上になります. 以上といっても項目的には1つしかなかったですが,PyTorch を使い始めた状態では意識できないポイントだったと思うので,初学者の方には十分参考になるんじゃないでしょうか. ここで述べたことに限らずメモリをうまく使う方法はあります. ただ PyTorch を使う時にメモリ不足に悩まされたら,無駄な計算グラフを作っていないか,無駄な計算グラフをいつまでも大切に持っておいていないか,そういう所を意識すると救われる場合があると思います. むしろそういう場合がほとんどだと思います. とりあえずこれだけでも意識しておくと良いのかなと個人的には思います.

研究で使うなら PyTorch? TensorFlow?

機械学習,というよりニューラルネットワークを用いた研究を行う場合,おそらく PyTorch か TensorFlow のどちらかを使うことになるでしょう. この2つは機械学習関連では2大巨頭であり,多くの研究で使われています. そのため先行研究のプログラムが PyTorch や TensorFlow で配布されている,という状況も多いです. しかしここで PyTorch と TensorFlow,どちらを使うのかという問題が浮上します.

基本的に先行研究がありプログラムが配布されているなら,そこで使われている言語やライブラリをそのまま使うのが楽でしょう. 知らない言語やライブラリだったとしても,既存手法の実装をしなくて良いというのは大きいです. そういった前提が無く自由に環境を選べるのであれば PyTorch を使った方がいいというのが,個人的な意見になります. 私は研究で TensorFlow と PyTorch 双方を使ったことがあり,この経験から PyTorch の方が研究向きだという結論になりました. この記事では,PyTorch の方がいいなという考えに至った経緯について述べていきます.

TensorFlow

まずは TensorFlow が何かについてです. TensorFlow は天下の Google 様が開発されたライブラリです. 様々な言語に対応していたりするので,Python が嫌いな人もにっこりです. 非常に簡単にネットワークを実装でき,また学習も少ない行数で行うことができます. なにせ fit という関数を呼ぶだけで学習することができます. 極端な言い方をすると 1 行で学習のプログラムを書けます.

またデフォルトで出力されるログが比較的リッチで見やすいというのも良いところです. こういった所から手軽に何かネットワークを実装し学習させてみる,という用途ではかなり使いやすい,というのが私が抱いている印象です.

ただし個人的にはこの手軽さが TensorFlow のデメリットだとも感じています. というのも内部の処理をいじるのが結構めんどうだったりするのです. 学習が 1 つの関数を呼び出すだけで良いということは,学習時の処理をカスタマイズするには,この関数の中身を変える必要があるということになります. これ研究という用途ではかなり面倒です.

研究用途で学習時の処理を変える場合,現在はどういう手順で学習が進んでいてどこをどう変えるかということを考えることになります. TensorFlow はデフォルトでいろいろやってくれているせいで,現在の学習手順の把握も結構手間がかかります. さらにその処理を適切に変更しようとすると,内部のコードを見たりする必要が出てきます(これは最悪の場合かも).

また損失関数の定義がリファレンスに明言されていないのも個人的にモヤっとしたポイントでもあります. TensorFlow のリファレンスで BinaryCrossEntropy の項目を見ると,引数の説明や実行例が記載されています. そんな中でどんな数式が実装されているかについては載っていません. つまり実行例からどんな数式が実装されているかを知れということになります(もしくはソースコードを読むか). 手軽に実行したりする場合は気にすることはないかもしれません. しかし研究であれば話は別です.

研究で使った損失関数がどんな数式か書けないというのは論外でしょう. 「自分の手法は TensorFlow のこの関数を損失関数にして実装しました」と言われても,式が無ければ考察のしようがありません. これは TensorFlow のことを熟知していれば問題ないかもしれませんが,バージョンが変わって破壊的な変更があった場合はそこも知らなければなりません.

PyTorch

PyTorch はオープンソースで開発されている Python 用のライブラリです. 対象となっている言語は Python のみですが,まぁ Python 以外を使う明確な理由はあまり無いように思うのでそこまで問題では無いでしょう. まぁ私は Python がそんなに好きではないので,積極的に使うことはないですが.

PyTorch は TensorFlow と比べると手軽さに欠けるというのが第一印象です. 学習をするには自分でループを回してパラメータの更新を定め,ログ等の出力も自前で実装する必要があります. この点で PyTorch は TensorFlow に劣るといって良いでしょう.

ですが研究用途という点で考えると,この手軽でないという要素が PyTorch の利点となります. 多くの処理を自分で実装する必要があるということは,カスタマイズが容易であるとも言うことができます. 学習手順もかなり簡単に変えることができる上に,勾配の値を確認したり好きなタイミングで好きな形式のログを出力できます. まぁやろうと思えば TensorFlow でもできると思いますが,PyTorch のほうが遥かに楽な印象です. わざわざ PyTorch 内部のプログラムを見る機会は TensorFlow より少なかったように思います.

またリファレンスにちゃんと数式が記載されているのも有り難いです. PyTorch のリファレンスで Binary Cross Entropy の項目を見ると引数等の説明に加え,実装されている数式が記載されています. おかげでどんな計算をしているかが一目でわかります. TensorFlow の節でも述べましたが,研究をしていて損失関数の実装時に数式を知らないというのは論外と言っていいでしょう. それがちゃんとリファレンスに記載されているというのは,研究用途で使う上でかなり大きなメリットになります.

まとめ

TensorFlow と PyTorch について,双方使ってみて抱いている個人的な印象を述べました. 端的に述べるなら以下のようになるでしょうか.

  • TensorFlow は手軽に実装して学習をしたりできるが,カスタマイズをしようとすると少し手間がかかる
  • PyTorch は手軽に実装しようとすると少し手間だが,カスタマイズ性が高くリファレンスが親切である

このカスタマイズ性が高くリファレンスが親切であるという2点は研究で用いる上で重要でしょう. 少なくとも私は重要だと思います. そのため研究用途で TensorFlow と PyTorch を選べる状況にあるのであれば,私は PyTorch を推奨します. 実際私が所属していた研究室では TensorFlow が不便で PyTorch に変えたという人もいたりしました. まぁ最終的には本人の判断にはなるので,あくまで私の意見です. これが誰かの参考になれば幸いです.

自作の TeX 用 Dockerfile

修論を書く時に TeX を使ったのですが,この TeX の環境構築,結構面倒臭いという印象が強いです. なにせバージョンだったり必要なパッケージだったりといろいろ考えないといけないことが多いです. さらに日本語を含むソースコードを扱おうとすると jlisting パッケージを自力でダウンロードしたりしないといけないです. 他にもフォントによってはちゃんと適切な場所に配置しないと,TeX が見つけてくれなかったりします.

ということで修論を書く時には TeX 環境を Docker に構築し,Docker 内の tex を実行することで pdf を生成するということを行っていました. これによって Docker さえあれば自由に環境構築ができるようになり,修論執筆のポータビリティが高くなって QOR (Quality Of Research) が爆上がりしていました.

この TeX 環境構築用の Dockerfile を作って使っていたわけですが,どこにも公開していないのもどうかと思ったので,この記事に書くことで供養しておきたいと思います.

この記事下部に Dockerfile,ビルド用の docker-compose.yml,実行に使える補助用のスクリプトを載せます. 自由に使ってください. 改変とかも好きにしていただいて結構です.

内容

いろいろ入ってます. 基本的には TeXLive を使っていて,パッケージマネージャ tlmgr を使っていろんなパッケージをインストールしています. 日本語を扱う上で必要なパッケージもある程度そろっているはずです. 少なくとも私はこのコンテナだけで修論を書ききりました.

tlmgr が参照する先が各年でアーカイブされたバージョンになっています. そのためコンテナをビルドするタイミングで,できあがる環境が変わるようなこともありません. また TeX英語圏で開発されているため,邦文を書くことを考慮した設計はあまり優先されません. (TeX ガチ勢かつ邦文環境ガチ勢でないと,邦文利用時のエラーを消しきれないため,日本語環境に完全に適用するのが困難) そのため無闇矢鱈にパッケージを更新すると,日本語でうまく動作しなくなる危険があります. しかし本 Dockerfile ではアーカイブされた過去の固定のバージョンをインストールするので,そういった危険性も抑えられています.

また tlmgr では管理できない jlisting パッケージもインストールしてあるので,日本語を含むソースコードも扱えます. それと日本語フォントとして Noto Sans,Noto Serif,源ノ角ゴシック (Source Han Sans),源ノ明朝 (Source Han Serif) の4つをインストールし,TeX が見つけられる場所に配置してあります. そのためこれらのフォントも使えるようになっています. これらは GoogleAdobe が開発した日本語フォントで,非常にきれいだったりするので,フォントに困っていたらとりあえずこれを使えばいいと思います. それと初期設定でフォントを埋め込むようにしているはずなので,生成された PDF には勝手に使ったフォントが埋め込まれるはずです.

とにかく言いたいのは,このコンテナを使っておくと変なところで悩まされずに TeX の環境が構築できるから,使うといいよっていう話です. ただし私が欲しいと思わなかった都合上,GUI の利用が一切想定されていません. 欲しい人は適宜 Dockerfile をいじるか,諦めるかしてください.

使い方

コンテナのビルド

下の方に載せている Dockerfile と docker-compose.yml ファイルを同じ階層に用意し下記コマンドを実行すれば,きれいに TeX の環境が整った Docker コンテナがビルドできます.

docker-compose build

ただし TeX を1から――いいえ,0からインストールするのでアホみたいに時間がかかります. また3GB くらいのクソデカいコンテナになるので,コンテナを作る場所は気をつけるようにしてください. 共用のサーバ上に配置すると(容量によりますが)迷惑になります.

また GUI の利用は想定していないので,基本的にコンソールでの操作になります.

既にインストールされていないパッケージをインストールしたい場合は,Dockerfile の下の方に下記のような行を追記し,ビルドしなおすことでインストールできます.

RUN tlmgr install <インストールしたいパッケージ名>

たぶん1番下の行に追記すれば良いはずです. ダメならちょっとずつ上に上げてみてください. このときのリビルドでは,そんなに時間がかかりません. これは Docker がキャッシュを持っているためです.

またビルドを繰り返していると,使っていない無名のコンテナが PC 上に乱立することになります. 適宜掃除してやって下さい. ストレージが大変なことになります. 特に共用のサーバ上に置いているような人は(ry

TeX のバージョンですが docker-compose.yml 内の下記の行における 2021 の部分を変更することで,別のバージョンを指定可能です.

        TEXLIVE_VERSION: 2021

記事の下に載せている物では 2021 になっているはずですが,この場合 2021 年にアーカイブされたパッケージ群がインストールされます. 動作確認ができているのは 2019 と 2020,それと 2021 の3年分です. 2022 年の分はファイルの配置が他の年と異なっていたので対応していません. 特に古いパッケージを使う理由が無いのであれば 2021 を使うことを推奨します. 特に jlreq というパッケージを使う場合は 2021 の利用を強く推奨します. 2020 以前だと jlreq の仕様が古く使いづらいです.

TeX を使う

下の方に載せている実行用のスクリプト (command.sh と命名すると仮定) を用意し,実行権限を与えて実行することでいろいろできます. ここで実行用のスクリプトの場所は任意です.

このスクリプトはカレントディレクトリをコンテナにマウントし,与えられたコマンドライン引数をそのまま実行するようになっています.

例えば作成した TeX ファイル (master.tex) と実行用のスクリプト (command.sh) が同じ階層にあるとします. この場合は下記コマンドを実行することで pdf を生成できます.

./command.sh uplatex master
./command.sh dvipdfmx master

つまり実行したいコマンドの前に ./command.sh をつけるだけです. TeX をコンソールで使う方法は適宜調べてください. 余裕があればブログ内にそれようの記事が生えるかもしれません.

実行用のスクリプト (command.sh)

#!/bin/sh

docker run --rm -u $(id -u):$(id -g) --volume "$(pwd)"://workdir tex_latex:latest $@

Dockerfile

FROM frolvlad/alpine-glibc:latest AS latex

ARG TEXLIVE_VERSION="2020"
ARG TEXDIR="/opt/texlive"
ARG TEXMFLOCAL="/opt/texlive/texmf-local"
ARG TEXMFSYSCONFIG="/opt/texlive/texmf-config"
ARG TEXMFSYSVAR="/opt/texlive/texmf-var"
ARG TEXMFHOME="~/texmf"
ARG TEXMFCONFIG="~/.texlive/texmf-config"
ARG TEXMFVAR="~/.texlive/texmf-var"
      # "TEXDIR /opt/texlive" \
      # "TEXMFLOCAL /opt/texlive/texmf-local" \
      # "TEXMFSYSCONFIG /opt/texlive/texmf-config" \
      # "TEXMFSYSVAR /opt/texlive/texmf-var" \
      # "TEXMFHOME ~/texmf" \
      # "TEXMFCONFIG ~/.texlive/texmf-config" \
      # "TEXMFVAR ~/.texlive/texmf-var" \
#ARG ARCHIVE="ftp://tug.org/historic/systems/texlive/"
ARG ARCHIVE="ftp://ftp.math.utah.edu/pub/tex/historic/systems/texlive/"

ENV PATH /opt/texlive/bin/x86_64-linuxmusl:$PATH

# texlive のインストール
RUN apk add --no-cache curl perl fontconfig-dev freetype-dev && \
    apk add --no-cache --virtual .fetch-deps xz tar && \
    mkdir /tmp/install-tl-unx && \
    curl -L ${ARCHIVE}${TEXLIVE_VERSION}/install-tl-unx.tar.gz | \
      tar -xz -C /tmp/install-tl-unx --strip-components=1 && \
    printf "%s\n" \
      "selected_scheme scheme-basic" \
      "" \
      "TEXDIR ${TEXDIR}" \
      "TEXMFLOCAL ${TEXMFLOCAL}" \
      "TEXMFSYSCONFIG ${TEXMFSYSCONFIG}" \
      "TEXMFSYSVAR ${TEXMFSYSVAR}" \
      "TEXMFHOME ${TEXMFHOME}" \
      "TEXMFCONFIG ${TEXMFCONFIG}" \
      "TEXMFVAR ${TEXMFVAR}" \
      "" \
      "binary_x86_64-darwin 0" \
      "binary_x86_64-linux 1" \
      "binary_win32 0" \
      "" \
      "option_doc 0" \
      "option_src 0" \
      "" \
      "in_place 0" \
      "option_adjustrepo 0" \
      "option_autobackup 0" \
      "option_backupdir tlpkg/backups" \
      "option_desktop_integration 0" \
      "option_file_assocs 0" \
      "option_fmt 1" \
      "option_letter 0" \
      "option_menu_integration 0" \
      "option_path 0" \
      "option_post_code 1" \
      "option_sys_bin /usr/local/bin" \
      "option_sys_info /usr/local/share/info" \
      "option_sys_man /usr/local/share/man" \
      "option_w32_multi_user 0" \
      "option_write18_restricted 1" \
      "portable 0" \
      "" \
      "tlpdbopt_install_docfiles 0" \
      "tlpdbopt_install_srcfiles 0" \
      > /tmp/install-tl-unx/texlive.profile && \
    /tmp/install-tl-unx/install-tl \
      --profile=/tmp/install-tl-unx/texlive.profile \
      --repository ${ARCHIVE}${TEXLIVE_VERSION}/tlnet-final/ && \
    rm -fr /tmp/install-tl-unx && \
    apk del .fetch-deps
RUN tlmgr install \
        collection-fontsextra \
        collection-fontsrecommended \
        collection-latexextra \
        collection-langjapanese \
        collection-luatex \
        collection-mathscience \
        collection-xetex \
        pxchfon plautopatch \
        platex platex-tools uplatex \
        pxjahyper pxpgfmark \
        biblatex biber \
        latexmk

RUN tlmgr install \
        mylatexformat
RUN tlmgr install \
        biblatex-chem \
        biblatex-phys \
        biblatex-nature \
        biblatex-science \
        biblatex-ieee

RUN apk --update add --no-cache netcat-openbsd unzip

# jlisting をダウンロードする
RUN mkdir -p ${TEXMFLOCAL}/tex/latex/jlisting && \
    curl -o ${TEXMFLOCAL}/tex/latex/jlisting/jlisting.sty.bz2 -O -L "https://osdn.net/frs/redir.php?m=nchc&f=mytexpert%2F26068%2Fjlisting.sty.bz2" && \
    bzip2 -d ${TEXMFLOCAL}/tex/latex/jlisting/jlisting.sty.bz2

# Noto Sans CJK jp
RUN curl -o /tmp/NotoSansCJKjp-hinted.zip -O -L https://noto-website-2.storage.googleapis.com/pkgs/NotoSansCJKjp-hinted.zip && \
    mkdir -p ${TEXMFLOCAL}/fonts/opentype/google/notosanscjk/ && \
    unzip -d ${TEXMFLOCAL}/fonts/opentype/google/notosanscjk/ /tmp/NotoSansCJKjp-hinted && \
    chmod 644 ${TEXMFLOCAL}/fonts/opentype/google/notosanscjk/*.otf
# Noto Serif CJK jp
RUN curl -o /tmp/NotoSerifCJKjp-hinted.zip -O -L https://noto-website-2.storage.googleapis.com/pkgs/NotoSerifCJKjp-hinted.zip && \
    mkdir -p ${TEXMFLOCAL}/fonts/opentype/google/notoserifcjk/ && \
    unzip -d ${TEXMFLOCAL}/fonts/opentype/google/notoserifcjk/ /tmp/NotoSerifCJKjp-hinted && \
    chmod 644 ${TEXMFLOCAL}/fonts/opentype/google/notoserifcjk/*.otf
# Source Han Sans JP
RUN curl -o /tmp/SourceHanSansJP.zip -O -L https://github.com/adobe-fonts/source-han-sans/releases/download/2.004R/SourceHanSansJP.zip && \
    mkdir -p ${TEXMFLOCAL}/fonts/opentype/adobe/sourcehansans && \
    unzip -d ${TEXMFLOCAL}/fonts/opentype/adobe/sourcehansans/ /tmp/SourceHanSansJP.zip
# Source Han Serif JP
RUN curl -o /tmp/SourceHanSerifJP.zip -O -L https://github.com/adobe-fonts/source-han-serif/releases/download/2.001R/12_SourceHanSerifJP.zip && \
    mkdir -p ${TEXMFLOCAL}/fonts/opentype/adobe/sourcehanserif && \
    unzip -d ${TEXMFLOCAL}/fonts/opentype/adobe/sourcehanserif/ /tmp/SourceHanSerifJP.zip

RUN mktexlsr

RUN kpsewhich NotoSerifCJKjp-Regular.otf
RUN kpsewhich NotoSansCJKjp-Black.otf
RUN kpsewhich SourceHanSerifJP-Regular.otf
RUN kpsewhich SourceHanSansJP-Regular.otf

WORKDIR /workdir

CMD ["sh"]

docker-compose.yml

version: "3"

services:
  latex:
    build:
      context: .
      target: latex
      args:
        TEXLIVE_VERSION: 2021
    tty: true
    image: tex_latex:latest
    container_name: latex

TeX って何という話

先日(一年前)に TeX のセットアップを手伝ったのですが,そもそも TeX をよく知らない状態だとどう使うのかわからないという問題があるなということを強く感じました. そこで私がわかる範囲で TeX について簡単にまとめておくことで,原因不明な自体への対処を簡単にしようという趣旨の記事です. 記事の趣旨上私の所属する大学の学科の学生に向けた内容になりますが,他の人が読めない記事になるわけではないと思うので自由に読んでいただければと思います.

TeX を使うメリットとデメリット

まずは TeX と呼ばれる物を使うことによるメリットとデメリットについて述べておきます. ここで述べるメリットとデメリットはあくまで私の主観です.

TeX を使うことのメリット

  • 文書作成が容易になる場合がある
    • 複雑な数式を容易に記述できる
    • 図番号や表番号,式番号等が自動で割り振られるためつまらないミスを気にする必要が減る

TeX を使うことのデメリット

  • 習熟にある程度の時間を要する
    • HTML 等を触れたことがある人には問題になりにくいだろうが,プログラミング言語に触れたことの無い人からすればかなり致命的と言える

TeX とは

そもそもの話,論文等は TeX で書け!と言われても TeX って何?という人は多いと思います. 工学系の大学では TeX の使い方くらいは教わるかもしれませんが,LaTeX とか upLaTeX とか出てくるときっとわけがわからなくなってしまうことでしょう. ということでちゃんと TeX って何だって話を考えていきます.

ところで,TeX と言った場合にはその文脈によって何を指しているのかが異なります. 非常におおざっぱに分類するなら言語を指すのかソフトウェアを指すのかという点で異なります. 実際のところ,TeX と言えばソフトウェアをイメージしていることが多いとは思いますが…

そして TeX と呼ばれるソフトウェアも複数存在し,それらは"TeX"というソフトウェアから派生したもの達のことを指します. TeX 系のソフトウェアというニュアンスですね. このソフトウェア達は組版処理システムなどと呼ばれており,PDF や本のデータを作るために作られています. この組版処理システムにどんな文字をどんな風に出力してほしいかを指定するために,TeX 言語等が使われています.

これらをまとめると

TeX と呼ばれているものは殆どの場合に組版処理システム群であり,そのシステムは TeX 言語等で書かれた TeX ファイルから PDF 等の文書を生成する.

という感じになります.

TeX 呼ばれる組版処理システム達

前述した通り TeX と呼ばれる組版処理システムは複数あります. これは開発された経緯上どうしようもないことかなとは思いますが,使う側からすると厄介この上ないですね. というわけで TeX と言ったときに考えられるソフトウェア達をいくつか列挙します.

  1. TeX
  2. LaTeX
  3. upLaTeX
  4. LuaLaTeX
  5. PDFLaTeX

とりあえず日本語利用者である我々はこの辺を知っておけば十分だと思います. 上から順に何ものなのかを見ていきましょう.

TeX の始祖 “TeX

一言で言うなら TeX は元祖 TeX です.

これは我らが Donald E. Knuth 先生が開発された TeX 系ソフトウェアの元祖です. しかし開発時期が非常に昔であるため,現在の標準的な使用に耐えられる設計とはとても言えず,ほとんどの人はこの "TeX" から派生したソフトウェアを使っていると思われます.

より現代的な TeXLaTeX

一言で言うなら LaTeX は使いやすくなった TeX です.

開発された経緯については,あまり把握できてないので述べません. ですが純粋な "TeX" と比べてより使いやすい形へと変化したとは思います.

日本語をを使える TeX が欲しい! “pTeX

一言で言うなら pTeX は日本語が使える TeX です.

もしかしたら既にご存知かも知れませんが,Knuth 先生は日本人ではありません. そのため Knuth 先生が開発された TeX というソフトウェアは日本語を書くことを想定して作られていません. これは日本人からすれば致命的な問題です. pTeX はその問題を解消するため TeX を拡張した物です. しかし,pTeXTeX の古い設計を多く持つ関係上現在使っている人はいないと言って良いでしょう.

Unicode と日本語を使う LaTeX “upLaTeX”

一言で言うなら Unicode と日本語を使うことができる LaTeX です. 日本語が使える uLaTeX とも言えます.

"TeX" を開発された Knuth 先生は日本人ではありません. つまり "TeX" は日本語を書くことを想定して作られたソフトウェアでは無いということになります. これは日本人からすれば致命的な問題です.

また "TeX" が開発されたのは 1970 年代から 1980 年代にかけてであり,Unicode なんてものは普及していません. 近年ほとんどのソフトウェアが Unicode を使えるというのに,組版処理で Unicode が使えないのは致命的な問題と言えるでしょう.

upLaTeX はその問題を解消するため LaTeX を拡張した物です. 2022 年現在,日本語の文書を作るのであれば upLaTeX がまず一つの選択肢にあがることとなるでしょう.

Lua 言語によって開発した TeX “LuaLaTeX”

一言で言うなら Lua 言語を使って開発された新たな LaTeX です.

2022 年現在,日本語の文書を作るとき upLaTeX だけでなく LuaLaTeX も十分候補に入ることでしょう.

直接 PDF が欲しい! “PDFLaTeX”

一言で言うなら不便な点を排除した LaTeX です.

2022 年現在,日本語の文書を作ることを考えると,PDFLaTeX は若干怪しいところがあると言えるでしょう.

日本語文書を書くならどれを使えばいいの?

現状だと upLaTex か LuaLaTeX のどちらかを使うのが妥当でしょう. 日本語を使えるかどうかという点でほとんど上記の2ついづれかになります. そのうえでどちらにするかという問題になります.

ここまでくるとほとんど好みになるかと思いますが,個人的には upLaTeX がいいのではないかと思います. 理由は upLaTeX の方が古い,というより普及しているからです. なにか問題に衝突した時,普及していれば検索するだけで簡単に問題に対処できます. また upLaTeX と LuaLaTeX では完全に互換性があるわけではなく,LuaLaTeX でできても upLaTeX ではできない,upLaTeX ではできても LuaLaTeX ではできない,というようなことがあったりします(できないはちょっと言いすぎかもしれないけれども…). そして upLaTeX の情報が大量にある中から LuaLaTeX の情報を探すのはちょっと大変だったりします. 以上から,よくわからないなら upLaTeX を使っておくのが安牌だろうというのが個人的な見解です.

TeX を使う流れ

さて,ここまで TeX ってどんな物なんだという概要を説明してきました. ではどうやって使うのかということについて説明していきます. また前提として upLaTeX を使うことを想定とします(私が LuaLaTeX よくわかってないので…).

まず最初に開発された TeX は 30 年以上前のソフトウェアです. 当時は PDF なんてファイル形式は存在していませんでした. そのため TeX というソフトウェア達の多くは,DVI というファイルを出力します. ですが,現在の文書ファイルの主流は PDF ファイルであり,DVI ファイルではありません. そのため我々は DVI ファイルを PDF ファイルに変換しなければなりません. つまり TeX を使って PDF の文書を書く流れは以下の図のようになります.

flowchart TB
TeXファイルを書く --> DVIファイルを生成する --> PDFファイルに変換する

最近のソフトウェアと比べるとひどい流れになっていますが,仕方ないです. そしてさらに面倒な手順がまっています. TeX の利点である図表や式の番号を自動で扱えるようになる,というのが問題になります. この図番号等を適切に割り当て,そして適切に参照するために複数回 DVI ファイルを生成する手順を踏む必要があるのです. この理由については述べませんが,状況によって何回 DVI ファイルを生成すればいいかは断定できません. 不十分な状態の DVI ファイルから PDF を作ると,図番号等の参照部分が "??" になったりします. 非常にやっかいです.

これは latexmk 等の補助ツールを使って回避するくらいしか方法が無いです. latexmk は設定ファイルに応じて必要なくなるまで DVI ファイルの生成を行い,最後に DVI を PDF に変換するツールです. これを使えばかなり TeX を使った文書作成が楽になったと記憶しています.

といっても aux というファイルが更新されなくなったときが正しい DVI ファイルが生成されたときなので,そこを自分で監視しておけば自力で対処できます. 私は自作の Makefile で必要なくなるまで DVI の生成を繰り返すようにしています.

さて,上図で説明した PDF を作る手順ですが,実は TeX が担うのは DVI の生成までです. PDF への変換は大抵 dvipdfmx というような別のソフトにやってもらいます. 他にも DVI を PDF に変換するソフトはありますが,2023 年現在では dvipdfmx 以外を使う利点は特に無いと思うのでわざわざとりあげません.

flowchart TB
TeXファイルを書く -->|upLaTeX| DVIファイルを生成する
DVIファイルを生成する -->|upLaTeX| DVIファイルを生成する
DVIファイルを生成する -->|dvipdfmx| PDFファイルに変換する

まとめ

とりあえずここまでのことを知っておけば自力での問題解決等が楽になるんじゃないでしょうか. 他にもいろいろ TeX にはアレな部分が多く,よく知らない状態では簡単にタブーに触れてしまったりします. ですがそんなことを気にしていては TeX なんてとても使えないです. 逆にちゃんと知ろうとすると,他のことに手を付ける時間が大幅に削られて本末転倒です(アホみたいに時間が消えます,研究どころじゃない). しかし理系だと TeX を使うのが事実上の標準で,論文を投稿するならほぼ間違いなく使うはめになります.

SATySFi なんていう最近の組版処理システムもありますが,普及度合いの問題でしばらくは TeX を使う必要があるでしょう. 下手をすると SATySFi は普及せずにいつまでも TeX が使われるかもしれません. しばらくは自分に必要な知識を適宜身につけて TeX とつきあっていくしかないでしょう. そしてこの記事がその助けになることを願います.