Smile Engineering Blog

ジェイエスピーからTipsや技術特集、プロジェクト物語を発信します

The Continuing Story of Error Correction Code 6

行列

はじめに

ここまでの記事で4ビットの送信語、 D_{4} D_{3} D_{2} D_{1}から単一パリティ符号やハミング符号を生成する、ということを考えてきました。
今回からはこれらの符号生成などを行列を使って表現する、ということをやってみたいと思います。
ところで行列、やったことありますか?
昔は高校の数学(数C、だったかな・・・)でも行列を扱っていましたがゆとり教育のあたりから高校では扱わなくなっていたんですね。
これが最近、国の「AI戦略」とやらで高校数学に復活するという話も出ていますね。
そんなこんなで行列、基礎からやってみましょう。
ただし、必要なものを必要なときにやっていく、という方向にしておきます。
まずは送信語を符号語に変換する、という部分を行列で表現できるようになりましょう。

そうだ、焼き鳥屋に行こう

とりあえず、焼き鳥屋に行ってビール飲みながら焼き鳥でも食べましょう。
お品書きは・・・

品名 値段
生ビール 500円
日本酒(一合) 300円
ねぎま 80円
きも 100円
なんこつ 100円
はつ 90円
かわ 80円

とりあえず生ビールね、さて何食べようかしらん。 あぁ、これが伝票ね。

品名 値段 注文数 小計
生ビール 500円 1 500円
日本酒(一合) 300円 0 0円
ねぎま 80円 0 00円
きも 100円 0 0円
なんこつ 100円 0 0円
はつ 90円 0 0円
かわ 80円 0 0円
合計 500円

生ビール来たよ、じゃあやきとりの注文ね。
ねぎま、きも、それからかわを2本ずつ。

品名 値段 注文数 小計
生ビール 500円 1 500円
日本酒(一合) 300円 0 0円
ねぎま 80円 2 160円
きも 100円 2 200円
なんこつ 100円 0 0円
はつ 90円 0 0円
かわ 80円 2 160円
合計 520円

伝票が2枚ありますが、この計算を式で表すと


(500円×1+300円×0+80円×0+100円×0+100円×0+90円×0+80円×0)\\
+(500円×0+300円×0+80円×2+100円×2+100円×0+90円×0+80円×2)\\
=(500円+0円+0円+0円+0円+0円)+(0円+0円+160円+200円+0円+160円)\\
=500円+520円\\
=1020円

となります。
これを行列で表してみます。

お品書き行列

まずはお品書きを行列で表します。
[と]あるいは(と)の間に値段を並べていきます。
この記事では[と]を使用して書いていきます。
それぞれの値段の区切りにカンマなどは使わないでスペースで区切っていきます。 一番左の列が生ビール、2番目の列が日本酒、3番目の列がねぎま、とお品書きの順番に単価をならべていきます。


\begin{bmatrix}
生ビールの単価 & 日本酒(一合)の単価 & ねぎまの単価 & きもの単価 & なんこつの単価 & はつの単価 & かわの単価
\end{bmatrix}

 
メニューの種類は7種類なので1行7列の行列になります。
実際の単価を入れた行列は以下のようになります。


\begin{bmatrix}
500 & 300 & 80 & 100 & 100 & 90 & 80
\end{bmatrix}

とりあえず生ビール伝票行列

最初に注文した生ビールの伝票を行列にしてみます。 こちらは注文数を縦にならべていきます。
一番上の行が生ビール、2番目の行が日本酒、3番目の行がねぎま、とお品書き行列の列に並べた順番と同じにします。


\begin{bmatrix}
生ビールの注文数\\
日本酒(一合)の注文数\\
ねぎまの注文数\\
きもの注文数\\
なんこつの注文数\\
はつの注文数\\
かわの注文数
\end{bmatrix}

 
メニューの種類は7種類なので7行1列の行列になります。
とりあえず生ビールの行列は以下のようになります。


\begin{bmatrix}
1\\
0\\
0\\
0\\
0\\
0\\
0\\
\end{bmatrix}

最初の食べ物オーダー伝票行列

最初に頼んだ焼き鳥の伝票も行列にしておきましょう。
とりあえず生ビール伝票行列と同じように縦に注文数をならべていきます。


\begin{bmatrix}
0\\
0\\
2\\
2\\
0\\
0\\
2
\end{bmatrix}

伝票を1枚にする

今、とりあえず生ビールの伝票と最初の食べ物オーダー伝票の2枚の伝票がありますが、面倒なので1枚の伝票にまとめてしまいましょう。
行列の足し算を行います。


\begin{bmatrix}
1\\
0\\
0\\
0\\
0\\
0\\
0\\
\end{bmatrix}+
\begin{bmatrix}
0\\
0\\
2\\
2\\
0\\
0\\
2
\end{bmatrix}=?

 

行列の足し算をどうやってやれば良いのか説明していませんが、話の流れからして各メニューごとの注文数を足し算すればよさそうですね。


\begin{bmatrix}
1\\
0\\
0\\
0\\
0\\
0\\
0\\
\end{bmatrix}+
\begin{bmatrix}
0\\
0\\
2\\
2\\
0\\
0\\
2
\end{bmatrix}=
\begin{bmatrix}
1\\
0\\
2\\
2\\
0\\
0\\
2
\end{bmatrix}

行列の足し算は各行列の同じ位置の要素を足すだけです。
足し算をするためには足す行列と足される行列の行数、列数が一致している必要があります。
ここではどちらも7行1列の行列なので足し算を実行することができます。
さて、これで伝票を一枚にすることができました。
次は行列で合計金額を計算してみましょう。

合計金額を求める

合計金額を求めるになどうしたら良いでしょうか。
普通に計算するなら


合計金額 = 生ビールの単価 × 生ビールの注文数 + 日本酒(一合)の単価 × 日本酒(一合)の注文数 + ねぎまの単価 × ねぎまの注文数 
+ きもの単価× きもの注文数+ なんこつの単価 × なんこつの注文数 + はつの単価 × はつの注文数 +かわの単価 × かわの注文数

 
ですよね。 これを行列で表現します。
まず、お品書き行列をおき、お品書き行列に伝票行列をかけます。


\begin{bmatrix}
生ビールの単価 & 日本酒(一合)の単価 & ねぎまの単価 & きもの単価 & なんこつの単価 & はつの単価 & かわの単価
\end{bmatrix}×
\begin{bmatrix}
生ビールの注文数\\
日本酒(一合)の注文数\\
ねぎまの注文数\\
きもの注文数\\
なんこつの注文数\\
はつの注文数\\
かわの注文数
\end{bmatrix}\\
=\begin{bmatrix}
合計金額
\end{bmatrix}

 
行列で掛け算を行う場合も普通に計算する場合と計算方法は同じです。
実際の値を入れて計算してみましょう。


\begin{bmatrix}
500 & 300 & 80 & 100 & 100 & 90 & 80
\end{bmatrix}×
\begin{bmatrix}
1\\
0\\
2\\
2\\
0\\
0\\
2
\end{bmatrix}\\
=\begin{bmatrix}
(500 × 1) + (300 × 0 ) + (80 × 2) + (100 × 2) + (100 × 0) + (90 × 0) + (80 × 2)
\end{bmatrix}\\
=\begin{bmatrix}
+500 + 0 + 160 + 200 + 0 + 0 + 160
\end{bmatrix}\\
=\begin{bmatrix}
1020
\end{bmatrix}

それぞれの小計(単価×注文数)を計算して、小計をすべて合計する、それだけですね。
行列の掛け算では掛けられる行列(ここではお品書き行列)の列数(ここでは7列)と掛ける行列(ここでは伝票行列)の行数(ここでは7行)が一致している必要があります。
そりゃそうですよね、伝票の8行目に注文数2と書いてあってもお品書き行列には8列目はないのだから一体何を注文したのかわからないですもんね。


\begin{bmatrix}
500 & 300 & 80 & 100 & 100 & 90 & 80
\end{bmatrix}×
\begin{bmatrix}
1\\
0\\
2\\
2\\
0\\
0\\
2\\
2(何を2個注文したの・・・?)
\end{bmatrix}\\

 
これだけでは普通に計算しているのと変わらないので2枚の伝票から計算してみましょうか。

伝票2枚バージョン

先ほどは2枚の伝票行列を足して1枚の伝票で計算しましたが伝票行列を2枚に分けてから計算してみましょう。
2枚の伝票を行列で表す場合、2列に並べて一つの行列とします。
1列目がとりあえず生ビール伝票、2列目が最初の食べ物オーダー伝票です。


\begin{bmatrix}
1 & 0\\
0 & 0\\
0 & 2\\
0 & 2\\
0 & 0\\
0 & 0\\
0 & 2
\end{bmatrix}

 
お品書き行列に伝票行列2枚バージョンを掛けてみます。


\begin{bmatrix}
500 & 300 & 80 & 100 & 100 & 90 & 80
\end{bmatrix}×
\begin{bmatrix}
1 & 0\\
0 & 0\\
0 & 2\\
0 & 2\\
0 & 0\\
0 & 0\\
0 & 2
\end{bmatrix}\\
=\begin{bmatrix}
(500 × 1) + (300 × 0 ) + (80 × 0) + (100 × 0) + (100 × 0) + (90 × 0) + (80 × 0) & (500 × 0) + (300 × 0 ) + (80 × 2) + (100 × 2) + (100 × 0) + (90 × 0) + (80 × 2) 
\end{bmatrix}\\
=\begin{bmatrix}
+500 + 0 + 0 + 0 + 0 + 0 + 0 & 0 + 0 + 160 + 200 + 0 + 0 + 160
\end{bmatrix}\\
=\begin{bmatrix}
500 & 520
\end{bmatrix}

 
はい、こんな感じになります。
先ほどと異なり答えは1行2列の行列になってますね。
そして、答えの1列目はとりあえず生ビール伝票の合計金額、2列目は最初の食べ物オーダー伝票の合計金額です。
行列の掛け算では答えの行列は掛ける行列(ここでは伝票行列2枚バージョン)の列数(ここでは2列)となります。
このため、伝票1枚のときの答えの行列は1列になっているわけです。

SAS備忘録

はじめに

SASについて本人の備忘録もかねてまとめていきます。

続きを読む

思考のサルベージ(その12)

各工程で心がけたい思想を掘り起こしてみる

今回は開発中の製品で見つけた不具合の過去製品への「フィードバック」について考えてみます。 ここでは、現在開発中の商品をPRODUCT-2、過去製品をPRODUCT-1と呼びましょう。PRODUCT-1はすでに客先リリース済みで、PRODUCT-2は開発も終盤でステム試験による不具合検出も落ち着いてきます。

PRODUCT-1の再リリース

PRODUCT-1をリリースした顧客から、今後の開発を踏まえPRODUCT-1に「機能C」を追加しての再リリースを要求されました。断るという選択肢はなかなかないのが現実で、スケジュールをやりくりして「機能C」を追加してシステム試験をやり直し、再度顧客に再リリースすることになります。

PRODUCT-2で検出した不具合

PRODUCT-2は、PRODUCT-1をベースに作った製品です。PRODUCT-2で検出したバグにはPRODUCT-1にそのまま残っているバグがあるかもしれません。「機能C」を追加するとともにPRODUCT-2で実施したバグ対策もPRODUCT-1にフィードバックできれば品質が向上し後の憂いがなくなります。

PRODUCT-1とPRODUCT-2の差分

フィードバックするにはPRODUCT-1とPRODUCT-2の機能差分を明確にする必要があります。 例えば、以下のような機能差分があるとしましょう。

  • PRODUCT-2には新規に「機能A」が追加されている
  • PRODUCT-1の「機能B」は「規格ver1.0」準拠
  • PRODUCT-2の「機能B」は「規格ver2.0」準拠

「機能A」についてはPRODUCT-2の独自仕様なのでこれに関するバグ対策はPRODUCT-1へのフィードバックは不要と判断できます。「機能B」についてはちょっと注意が必要で、「規格ver2.0」固有の部分についてのバグ対策はフィードバック不要ですが、「規格ver1.0」「規格ver2.0」共通部分のバグ対策については要フィードバックとしなければなりません。 まとめるとフィードバックが必要なのは以下のようにまとめられます。

  • 「機能B」の「規格ver1.0」と「規格ver2.0」の共通部分のバグ対策
  • 「機能A」を除いたその他のバグ対策

フィードバック

どこの現場でもたいてい便利なバージョン管理システムや不具合管理システムを連携させて使っているので、どのヴァージョンでどんなをバグ対策をしたか履歴を見れば一目でわかるでしょう。要フィードバックの条件に合致する対策をPRODUCT-1に取り込んでいけばよいだけです。

PRODUCT-1に「機能C」を取り込み、要フィードバックな対策もすべて入れました。フィードバック項目はすべてPRODUCT-2のシステム試験で検証済みなので、再リリース向けPRODUCT-1のシステム試験は「機能C」関連以外はすんなり通るはずでしたが、「機能B」の「規格ver1.0」と「規格ver2.0」の共通部分で引っかかってしまいました。

ちゃんと運用できていますか?

こういうことでした。

PRODUCT-2のシステム試験で「規格ver2.0準拠の機能B」で処理速度が遅いという不具合がありました。処理性能の改善ということで、様々なトライアルをして幾度も評価を繰り返していくうちにいくつかバグが見つかりました。これらのバグが全て「「規格ver2.0準拠の機能B」で処理速度が遅い」という不具合として一括でバージョン管理システムにも不具合管理システムにも登録されていました。その中に1件、「規格ver1.0」「規格ver2.0」共通部分のバグが埋もれていました。正しくは、処理速度の不具合とは別に不具合登録され、別バージョンで対応すべきでした。

何か掘り起こせた?

  • 運用ルールを明確に。
  • 不具合登録、対策は手間がかかっても各事案ごとに。

せっかく便利なツールあるのに、運用が正しくなかったがために余計な工数を費やすという結果を招きました。今かける手間暇は、近い未来の手間暇を省いてくれる投資みたいなもんですね。

おしまい

でも時々あるんですよね、システム試験チームも設計チームも誰一人、正常な判断能力を失ってしまっている時って。

Singularityでお手軽コンテナ運用! 〜CUDAも使えます〜

Singularityってなに!?

今回はSingularityを使って、コンテナ運用する方法を紹介します。

singularity.lbl.gov

って、そもそもそれは一体何!?と思われる方も多いかと思いますが、一言で言うとDockerのようなコンテナフレームワークです。
Singularityには下記のような特徴があります。

  • 主に一般ユーザーで使用することを想定しているためGPUも扱いやすい
  • Dockerイメージをそのまま使用可能のためDockerHUBも使用可能

特に一番目の理由は特に重要のようで、GPUで扱いやすい故、HPCなどで使用されるケースが多いようです。
コンテナ起動時、オプション一つでGPUが使えるようになるのは魅力的ですね。(詳細後述)

  • Singularityってなに!?
  • インストール方法
  • Docker HubからSingularity用コンテナイメージをビルドしよう
    • ビルド方法
    • 実行方法
  • CUDAも使ってみよう!
    • TensorFlowコンテナイメージのビルド
    • GPUモードでシェルの呼び出し
  • まとめ
続きを読む

Node.js のインストール

はじめに

node のバージョン管理に n を使っています。今までは n を npm で導入していて、そのためには先ず node/npm をシステムにインストールする必要があったのですが、これ無しに、直接 n をインストール方法を知ったので備忘のために記します。

直接インストールには n-install というサードパーティを使用します。

続きを読む

Audacityの「ノイズ除去」を検証してみる

Audacityでノイズ除去」では AudacityのNoise Reductionを紹介しましたが、はたしてどんなアルゴリズムなのでしょうか。気になったので少し検証してみます。

  • Audacityソースコード
  • 特定の周波数をノイズとして消してみる
    • 1kHzの正弦波を生成
    • 2kHzの正弦波を生成(トラックの追加)
    • 1kHzと2kHzの波形を合成
      • 周波数特性
    • 1kHzの正弦波でノイズプロファイルを取得
    • ノイズの除去:デフォルトパラメータ(ノイズ除去=12dB, 感度=6, 周波数平滑化=3)
      • 周波数特性を見る
    • ノイズの除去:パラメータを大きく(ノイズ除去=24dB, 感度=6, 周波数平滑化=3)
      • 周波数特性を見る
続きを読む

The Continuing Story of Error Correction Code 5

ハミング符号再訪

ハミング符号は誤り訂正の基礎

The Continuing Story of Error Correction Code 2 - Smile Engineering Blog で7ビットハミング符号を構成してみました。
このハミング符号はSECDED、CRC符号、BCH符号、リードソロモン符号などの基礎となっています。
ハミング符号に色々なアイデアを追加して更に強力な訂正符号が作り出されていったわけですね。
これらの符号を理解するためにハミング符号をもっと深堀してみましょう。 今回は訂正符号で頻繁に登場する排他論理和(XOR)について見ていきます。

排他論理和

入力をA、Bとしたときに出力Xが以下のようになる演算が排他論理和です。

 A  B  X
0 0 0
0 1 1
1 0 1
1 1 0

なんか、この出力の並びどこかで見たような・・・
The Continuing Story of Error Correction Code 2 - Smile Engineering Blog の「検査符号の計算方法」で登場したこの演算。


0+0=0 \\
0+1=1 \\
1+0=1 \\
1+1=0

これですね。
排他論理和は桁上がりを無視したビットの足し算になっています。
2を法とした演算ですね。


2 \equiv 0 (mod 2)

なので 1 + 1 = 0となります。

ちなみに引き算はどうなってるんよ

とりあえず普通に引き算をしてみましょう。


0-0=0 \\
0-1=-1 \\
1-0=1 \\
1-1=0

ここで 0 - 1 -1になってしまいますが、 0 1しかないはずなのに -1ってどうしたらいいんでしょう?
足し算を見てみると計算のルールとして 1 + 1 = 0と定義しているので


1 + 1 = 0 \\
1 =-1

より 0 - 1 = 1としてしまいます。
よって、引き算は以下のようになります。


0-0=0 \\
0-1=1 \\
1-0=1 \\
1-1=0

この演算、足し算と見比べてみると演算の記号が +から -に変わっただけで他は同じですね。
このビット単位の演算では足し算も引き算も結果は同じになります。
引き算も2を法とした演算と考えれば


1 \equiv -1 (mod 2)

となるので 0 - 1 = 1となります。

排他論理和の不思議な性質

The Continuing Story of Error Correction Code 2 - Smile Engineering Blogで作ったハミング符号 P_{3} P_{2} P_{1}


(P_{3},P_{2},P_{1})

と表すことにします。 P_{3}=1 P_{2}=0 P_{1}=1なら


(1,0,1)

となります。

この3ビットのパリティを加算します。加算は先ほど示したルールに従いビットごとに行います。
3ビットのパリティを3個適当に取り出して加算してみましょう。
とりあえず (1,0,1) (0,0,1) (0,1,1)の3個を選んでみました。


(1,0,1)+(0,0,1)+(1,1,0)=(0,1,0)

答えは (0,1,0)ですね。 ではこの (0,1,0)に再度 (1,0,1) (0,0,1) (0,1,1)を加算してみます。


(0,1,0)+(1,0,1)+(0,0,1)+(1,1,0)=(0,0,0)

答えは (0,0,0)になります。 加算した結果に更に同じものを加算すると答えは0になるんですね。
なんか、不思議な感じがしますがこの演算は加算と減算は同じである、ということを思い出すと納得がいきます。


(1,0,1)+(0,0,1)+(1,1,0)=(0,1,0) \\
(0,1,0)-(1,0,1)-(0,0,1)-(1,1,0)=(0,0,0)

値を加算して、同じ値を減算すれば0になる、という当たり前の話です。
この当たり前が、ビット単位の演算でも成立しているよ、と。

では3個足して、その答えに2個だけ更に加算(減算)するとどうなるでしょうか?  (1,0,1)を加算(減算)しないと・・・


(1,0,1)+(0,0,1)+(1,1,0)=(0,1,0) \\
(0,1,0)+(0,0,1)+(1,1,0)=(1,0,1)

ちゃんと加算(減算)しなかった1個 (1,0,1)が残ってますね。 加算と減算の結果が同じということを除くと普通の加算、減算と同じように考えてもよさそうです。

排他論理和ハミング符号を考えてみる

The Continuing Story of Error Correction Code 2 - Smile Engineering Blogハミング符号は4ビットの送信語がありました。  D_{4} D_{3} D_{2} D_{1}の4ビットです。
この4ビットのうち1ビットだけが1の時の符号語を取り出してみます。

 D_{4}  D_{3}  D_{2}  P_{3}  D_{1}  P_{2}  P_{1}
0 0 0 0 1 1 1
0 0 1 1 0 0 1
0 1 0 1 0 1 0
1 0 0 1 0 1 1

 D_{1}だけが1のときのパリティ (0,1,1) D_{2}だけが1のときのパリティ (1,0,1) D_{3}だけが1のときのパリティ (1,1,0)、 そして D_{4}だけが1のときのパリティ (1,1,1)になっています。

では D_{1} D_{2}が1のときのパリティは?

 D_{4}  D_{3}  D_{2}  P_{3}  D_{1}  P_{2}  P_{1}
0 0 1 1 1 1 0

になっているから (1,1,0)!!
正解!!、なんですが実は D_{1}だけが1のときのパリティ D_{2}だけが1のときのパリティから計算できるのです。
計算方法は D_{1}だけが1のときのパリティ D_{2}だけが1のときのパリティを加算する、だけです。
やってみましょう。
 D_{1}だけが1のときのパリティ (0,1,1) D_{2}だけが1のときのパリティ (1,0,1)だから


(0,1,1)+(1,0,1)=(1,1,0)

もう一個ぐらいやってみましょうか。
送信語の全ビットが1の場合は D_{1}から D_{4}までそれぞれだけが1のときのパリティを加算します。


(0,1,1)+(1,0,1)+(1,1,0)+(1,1,1)=(1,1,1)

表をみると確かに (0,1,1)になっていますね。

 D_{4}  D_{3}  D_{2}  P_{3}  D_{1}  P_{2}  P_{1}
1 1 1 1 1 1 1

では、符号語に1ビットの誤りがあった場合を考えてみます。
送信語の全ビットが1を送信したら D_{3}が誤って0になったとします。
符号語は (1,1,1,1,1,1,1)ですが、受信語は (1,0,1,1,1,1,1)になりました。
受信語の D_{4} D_{2} D_{1}が1、パリティ (1,1,1)なので


(1,1,1)+(0,1,1)+(1,1,0)+(1,1,1)=(1,0,1)

で、答えは (1,0,1)、これは D_{3}だけが1のパリティに該当します。
ということで誤っているのは D_{3}のビットだとわかります。

なんか分かったような、分からんような・・・

延々と言葉で説明してきましたが、そろそろ数学的な表現を導入してハミング符号を表現できるようにしましょう。
まずは、あれですね。
みんな大好き「行列」。
え゛ーーー、って声が聞こえそうですが、慣れるとこのほうが楽ですよ。
いや、本当に。
ということで次回は行列の基礎。
ハミング符号を行列を使って表してみましょう!!