ラグランジュの未定乗数法

100日後に数学ができるようになる僕。

制約条件と等高線のグラフ

import matplotlib.pyplot as plt
import numpy as np

x = np.arange(-1, 1, 0.01)
y = np.arange(-1, 1, 0.01)

X, Y = np.meshgrid(x, y)
Z = 2 * X ** 2 + 3 * Y ** 2

plt.contourf(X, Y, Z, levels=20, cmap='gray')
plt.colorbar()

plt.plot(x, 1 - x, color='red', label='x + y = 1')
plt.legend()

plt.annotate('Local minimum', xy=(3/5, 2/5), xytext=(-1/4, 1/4), color='red',
  arrowprops=dict(arrowstyle='->',
  color='red',
  connectionstyle='angle3, angleA=0, angleB=-90'))

plt.axis([-1, 1, -1, 1])

plt.show()

Matplotlibはじめました

最近、『Pythonデータサイエンスハンドブック ―Jupyter、NumPy、pandas、Matplotlib、scikit-learnを使ったデータ分析、機械学習』と『最短コースでわかる Pythonプログラミングとデータ分析』を読んでいる。

特にかれるのが Matマットplotプロットlibリブ というグラフ描画ライブラリで、巧く使えばかなり複雑なデータもそこそこの品質で可視化できることには驚いた。

実際に手を動かしてりをしてみたので、備忘録として以下にまとめてみる。

基本の正弦波

まずは {\sin (x)} をデフォルト設定のままプロットする。

import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 10, 100)
plt.plot(x, np.sin(x))
plt.show()


線の色

次に、線の色を変えてみる。 16進数指定のほか、HTMLで用いられる色名を直接指定することもできる。

import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 10, 100)
plt.plot(x, np.sin(x), color='#007f00')  # 色を指定
plt.show()


軸の範囲

さらに、軸の範囲を明示的に指定することもできる。もともと {x} の範囲は {0 \leq x \leq 10} であったのを、{0 \leq x \leq 2\pi} の範囲に設定して描画する。

{x} 軸の目盛りの最大値が 6.3弱 ({=2\pi}) になっているところに注目。

import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 10, 100)

plt.plot(x, np.sin(x))
plt.xlim(0, 2 * np.pi)  # x軸の範囲を 0 ≦ x ≦ 2π に設定
plt.show()


軸の縮尺

ここまでの例では、{x}軸と{y}軸のアスペクト比が 1:1 ではなく、使途によってはあまり具合のよいものとはいえない場合がある。

そこで、2軸の縮尺を揃えてみよう。

import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 10, 100)

plt.plot(x, np.sin(x))
plt.axis('equal')  # x軸とy軸の縮尺を一致させる
plt.xlim(0, 2 * np.pi)
plt.show()


軸の間隔

{x}軸の目盛りの間隔が、1きざみになってしまっている。 本来は、{\displaystyle\frac{\pi}{4}}ずつ目盛りを刻みたいとしよう。

import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 10, 100)

ax = plt.axes()

ax.plot(x, np.sin(x))
ax.axis('equal')

ax.xaxis.set_major_locator(plt.MultipleLocator(np.pi / 2))
ax.xaxis.set_minor_locator(plt.MultipleLocator(np.pi / 4))

ax.set_xlim(0, 2 * np.pi)
plt.show()


目盛のカスタマイズ

目盛りの刻み幅は {\displaystyle\frac{\pi}{4}} となったが、肝心のラベルが小数表示になってしまっており、いまひとつ格好が良くない。

本来は {n \pi}のように、{\pi}の何倍の地点にあるかを一目で判るようにするため、目盛りの表示をカスタマイズしてみる。

import matplotlib.pyplot as plt
import numpy as np

def format_func(value, tick_number):
  N = int(np.round(2 * value / np.pi))
  if N == 0:
    return '0'
  elif N == 1:
    return r'$\pi/2$'
  elif N == 2:
    return r'$\pi$'
  elif N % 2 > 0:
    return r'${0}\pi/2$'.format(N)
  else:
    return r'${0}\pi$'.format(N // 2)

x = np.linspace(0, 10, 100)

ax = plt.axes()

ax.plot(x, np.sin(x))
ax.axis('equal')

ax.xaxis.set_major_locator(plt.MultipleLocator(np.pi / 2))
ax.xaxis.set_minor_locator(plt.MultipleLocator(np.pi / 4))

ax.xaxis.set_major_formatter(plt.FuncFormatter(format_func))

ax.set_xlim(0, 2 * np.pi)
plt.show()


注釈

最後に、グラフの中に注釈を追加してみる。

LaTeX でレポートや論文を書く場合は、overpic.sty などで図版の上から文字を重ね打ちする技法も使えるが、簡単な注釈ならば Matplotlib で入れることもできる。

import matplotlib.pyplot as plt
import japanize_matplotlib  # 日本語を有効化する
import numpy as np

def format_func(value, tick_number):
  N = int(np.round(2 * value / np.pi))
  if N == 0:
    return '0'
  elif N == 1:
    return r'$\pi/2$'
  elif N == 2:
    return r'$\pi$'
  elif N % 2 > 0:
    return r'${0}\pi/2$'.format(N)
  else:
    return r'${0}\pi$'.format(N // 2)

x = np.linspace(0, 10, 100)

ax = plt.axes()

ax.plot(x, np.sin(x))
ax.axis('equal')

ax.xaxis.set_major_locator(plt.MultipleLocator(np.pi / 2))
ax.xaxis.set_minor_locator(plt.MultipleLocator(np.pi / 4))

ax.xaxis.set_major_formatter(plt.FuncFormatter(format_func))

# 注釈を追加
ax.annotate('極小', xy = (3 * np.pi / 2, -1), xytext = (3.5, -1.5),
            arrowprops = dict(arrowstyle = '->',
                              connectionstyle = 'angle3,angleA=0,angleB=-90'))

ax.set_xlim(0, 2 * np.pi)
plt.show()

Widrow-Hoffの学習規則

ふたたび『わかりやすいパターン認識』から、Widrow-Hoffの学習規則。

パーセプトロンの学習規則では反復計算が収束しないような、特徴の分布がオーバラップするケースでも、なんとか識別境界を定めてくれる。

いつもはiPad miniでお絵描きをしているが、今回はちょっと思い立ってiPad Pro (12.9) を引っ張り出してきた。

ひろびろ使えて良き。

パターン認識

オーム社の『わかりやすいパターン認識(第2版)』を読んでる。

タイトルに『わかりやすい』とあるが、いかんせん大学生向けの教科書なので、数式が乱舞する内容となっており、初学者のモチベーションを打ち砕きかねない。

どうにかしてこれを直感的に理解するためのアプローチに書き換えられないか思案中である。

最近傍決定則は、各クラスの代表値同士が互角に撃ち合ったときにできる国境線を識別境界として採用するイメージ。

パーセプトロンの学習規則は、識別境界の法線ベクトルを、レバーのようにぐりぐり操作するイメージ。


区分的線型識別関数は、各クラスに複数の代表値を立てて、最近傍決定則と同じくよそと撃ち合いをするイメージ。

このあと、Widrow-Hoffの学習規則もなんとか直感的理解に至るイメージを考えたいが、そのためには、最小二乗法と最急降下法の世界を旅しなくてはならない。

はてなブログで ResizeObserver と SVG のオーバレイを組み合わせる

はてなブログMarkdown モードでは、HTML を直接書けることが知られているが、記事の中に SVG を直接マークアップしたり <script> タグを書けることはあまり知られていない。

しかし、これらを巧く使うと、たとえば関連する単語間を線で結ぶといったギミックに対応できる。

don't 
trust 
any 
candidate
.
To 
begin 
with, 
they
 
always 
make 
promises 
they
 
don't 
keep 
.

 

さらに、ブラウザの幅が変わって改行位置が変わっても、線の端点の座標が再計算される。

ソース

  <div id="myContainer" style="display: flex; flex-direction: column; position: relative; gap: 1rem;">
    <div style="display: flex; flex-direction: row; flex-wrap: wrap;">
      <div style="flex: 0 0 auto;">I&nbsp;</div>
      <div style="flex: 0 0 auto;">don't&nbsp;</div>
      <div style="flex: 0 0 auto;">trust&nbsp;</div>
      <div style="flex: 0 0 auto;">any&nbsp;</div>
      <div style="flex: 0 0 auto; display: flex; z-index: 1">
        <div style="flex: 0 0 auto" id="candidate">candidate</div>.
      </div>
    </div>
    <div style="display: flex; flex-direction: row; flex-wrap: wrap;">
      <div style="flex: 0 0 auto;">To&nbsp;</div>
      <div style="flex: 0 0 auto;">begin&nbsp;</div>
      <div style="flex: 0 0 auto;">with,&nbsp;</div>
      <div style="flex: 0 0 auto; display: flex; z-index: 1">
        <div style="flex: 0 0 auto;" id="they1">they</div>&nbsp;
      </div>
      <div style="flex: 0 0 auto;">always&nbsp;</div>
      <div style="flex: 0 0 auto;">make&nbsp;</div>
      <div style="flex: 0 0 auto;" id="promises1">promises&nbsp;</div>
      <div style="flex: 0 0 auto; display: flex; z-index: 1">
        <div style="flex: 0 0 auto;" id="they2">they</div>&nbsp;
      </div>
      <div style="flex: 0 0 auto;">don't&nbsp;</div>
      <div style="flex: 0 0 auto;">keep&nbsp;</div>
      <div style="flex: 0 0 auto; border: 1px solid #cccccc;" id="promises2"></div>
      <div style="flex: 0 0 auto;">.</div>
    </div>
    <div id="overlay" style="position: absolute; left:0; right:0; top:0; bottom:0;">
      <svg id="svg" x=0 y=0>
        <line id="line1" stroke="#cccc00" stroke-width="2" />
        <line id="line2" stroke="#cccc00" stroke-width="2" />
      </svg>
    </div>
  </div>
  <script>
    
    const resizeObserver = new ResizeObserver(entries => {
      const promises1 = document.getElementById('promises1');
      const promises2 = document.getElementById('promises2');
      promises2.style.width = `${promises1.clientWidth}px`;

      const overlay = document.getElementById('overlay');
      const svg = document.getElementById('svg');

      svg.setAttribute('width', `${overlay.clientWidth}`);
      svg.setAttribute('height', `${overlay.clientHeight}`);

      const candidate = document.getElementById('candidate');
      const x1 = candidate.offsetLeft + candidate.clientLeft + candidate.clientWidth * 0.5;
      const y1 = candidate.offsetTop + candidate.clientTop + candidate.clientHeight * 0.5;

      candidate.style.backgroundColor = 'white';
      candidate.style.border = '1px solid #cccc00';

      const they1 = document.getElementById('they1');
      const x2 = they1.offsetLeft + they1.clientLeft + they1.clientWidth * 0.5;
      const y2 = they1.offsetTop + they1.clientTop + they1.clientHeight * 0.5;

      const they2 = document.getElementById('they2');
      const x3 = they2.offsetLeft + they2.clientLeft + they2.clientWidth * 0.5;
      const y3 = they2.offsetTop + they2.clientTop + they2.clientHeight * 0.5;

      they1.style.backgroundColor = 'white';
      they1.style.border = '1px solid #cccc00';

      they2.style.backgroundColor = 'white';
      they2.style.border = '1px solid #cccc00';

      const line1 = document.getElementById('line1');
      line1.setAttribute('x1', x1);
      line1.setAttribute('y1', y1);
      line1.setAttribute('x2', x2);
      line1.setAttribute('y2', y2);

      const line2 = document.getElementById('line2');
      line2.setAttribute('x1', x1);
      line2.setAttribute('y1', y1);
      line2.setAttribute('x2', x3);
      line2.setAttribute('y2', y3);
    });
    resizeObserver.observe(document.getElementById('myContainer'));
  </script>

この文字列に対して、(^\s+)|(\n)'' に置換して、余分なインデントや改行を消し、Markdown モード or はてなブログモードの編集エリアにペーストする。

The Fab Four Technique

基礎

メディアクエリなしでレスポンシブなレイアウトを組むテクニックの一つ。

コンテナの横幅がある一定のブレイクポイントを下回った場合は、<div> コンテナひとつ丸ごと非表示にする。

<!-- ブレイクポイントを 500px に設定 -->
<div style="width: 100%; display: inline-flex; flex-direction: row;">
  <div
    style="background-color:aqua; min-width: 0; width: calc((100% - 500px) * 1000); max-width: 80px; overflow: hidden;">
    &#8203;
  </div>
  <div
    style="background-color: red; min-width: calc(100% - 80px); width: calc((500px + 0.5px - 100%) * 1000); max-width: 100%;">
    <div>
      main
    </div>
  </div>
</div>

反対バージョン。

<div style="width: 100%; display: inline-flex; flex-direction: row-reverse;">
  <div
    style="background-color:aqua; min-width: 0; width: calc((100% - 500px) * 1000); max-width: 80px; overflow: hidden;">
    &#8203;
  </div>
  <div
    style="background-color: red; min-width: calc(100% - 80px); width: calc((500px + 0.5px - 100%) * 1000); max-width: 100%;">
    <div>
      main
    </div>
  </div>
</div>

なぜこんな回りくどいことをしようとしたかというと、やはり吹き出しテンプレートのためである。 style 属性にはメディアクエリが使えない、それでもレスポンシブ対応したい、ということで、なんとかできないかと苦心の末の結論である。

応用

サンプルです。サンプルです。サンプルです。サンプルです。サンプルです。サンプルです。サンプルです。サンプルです。サンプルです。
サンプルです。サンプルです。サンプルです。サンプルです。サンプルです。サンプルです。サンプルです。サンプルです。サンプルです。
<!-- たーせるくん(ボトムマージン調整版)-->
<div style="width:100%;display:inline-flex;flex-direction:row;"><div style="margin:0;padding:0;border:none;min-width:0;width:calc((100% - 540px)*1000);max-width:120px;overflow:hidden;">&#8203;</div><div style="min-width:calc(100% - 120px);width:calc((540px + 0.5px - 100%)*1000);max-width:100%;margin:0;margin-bottom:3px;padding:0;border:none;display:grid;grid-template-columns:1fr minmax(1rem,max-content) 20px max-content;"><div style="grid-column:1/2;"></div><div style="grid-column:3/4;padding-top:48px;"><div style="width:0;height:0;border-style:solid;border-width:0px 0 8px 20px;border-color:transparent transparent transparent #d7d6d6;position:relative;z-index:1"></div></div><div style="grid-column:4/5;width:80px;height:80px;margin:0;margin-bottom:-1rem;padding:0;background-image:url('https://cdn-ak.f.st-hatena.com/images/fotolife/t/tercel_s/20200113/20200113211534.png');background-repeat:no-repeat;background-size:contain;"></div><div style="grid-row:1/2;grid-column:2/3;display:grid;grid-template-rows:40px max-content;"><div></div><div style="margin:0;margin-left:3px;padding:0;padding-right:0.3rem;padding-left:0.3rem;border-radius:0.3rem;color:#412318;background:#d7d6d6;box-shadow:0 1px 3px #412318;font-size:0.9rem;"><!-- ここに吹き出しの内容を HTML で書いてください --></div></div></div></div>
<!-- たーせるくん(ボトムマージン非調整版)-->
<div style="width:100%;display:inline-flex;flex-direction:row;"><div style="margin:0;padding:0;border:none;min-width:0;width:calc((100% - 540px)*1000);max-width:120px;overflow:hidden;">&#8203;</div><div style="min-width:calc(100% - 120px);width:calc((540px + 0.5px - 100%)*1000);max-width:100%;margin:0;margin-bottom:3px;padding:0;border:none;display:grid;grid-template-columns:1fr minmax(1rem,max-content) 20px max-content;"><div style="grid-column:1/2;"></div><div style="grid-column:3/4;padding-top:48px;"><div style="width:0;height:0;border-style:solid;border-width:0px 0 8px 20px;border-color:transparent transparent transparent #d7d6d6;position:relative;z-index:1"></div></div><div style="grid-column:4/5;width:80px;height:80px;margin:0;padding:0;background-image:url('https://cdn-ak.f.st-hatena.com/images/fotolife/t/tercel_s/20200113/20200113211534.png');background-repeat:no-repeat;background-size:contain;"></div><div style="grid-row:1/2;grid-column:2/3;display:grid;grid-template-rows:40px max-content;"><div></div><div style="margin:0;margin-left:3px;padding:0;padding-right:0.3rem;padding-left:0.3rem;border-radius:0.3rem;color:#412318;background:#d7d6d6;box-shadow:0 1px 3px #412318;font-size:0.9rem;"><!-- ここに吹き出しの内容を HTML で書いてください --></div></div></div></div>
<!-- ネコフード -->
<div style="width:100%;display:inline-flex;flex-direction:row-reverse;"><div style="margin:0;padding:0;border:none;min-width:0;width:calc((100% -   540px)*1000);max-width:120px;overflow:hidden;">&#8203;</div><div style="min-width:calc(100% - 120px);width:calc((540px +  0.5px - 100%)*1000);max-width:100%;margin:0;margin-bottom:3px;padding:0;border:none;display:grid;grid-template-columns:max-content 20px minmax(1rem,max-content);"><div style="width:80px;height:80px;margin:0;padding:0;background-image:url('https://cdn-ak.f.st-hatena.com/images/fotolife/t/tercel_s/20200112/20200112212801.png');background-repeat:no-repeat;background-size:contain;"></div><div style="padding-top:48px;"><div style="width:0;height:0;border-style:solid;border-width:0px 20px 8px 0;border-color:transparent #c6bab6 transparent transparent;position:relative;z-index:1"></div></div><div style="display:grid;grid-template-rows:40px max-content;"><div></div><div style="margin:0;margin-right:3px;padding:0;padding-left:0.3rem;padding-right:0.3rem;border-radius:0.3rem;color:#412318;background:#c6bab6;box-shadow:0 1px 3px #412318;font-size:0.9rem"><!-- ここに吹き出しの内容を HTML で書いてください --></div></div></div></div>