---
# System prepended metadata

title: Machine Learning Cheat Sheet
tags: [Survey]

---

---
lang: ja-jp
tags: Survey
title: Machine Learning Cheat Sheet
---
# Machine Learning Cheat Sheet
- [Applications](#Applications)
- [Notation](#Notation)
- [Framework](#Framework)
- [Kernel Method](#Kernel-Method)
- [Models](#Models)
- [Optimization](#Optimization)
- [Appendix](#Appendix)

## Advanced
- [Optimization](/xa-NO855Q5CW521BUuL1wg)

## Applications
- [Ranking](https://hackmd.io/@moriaki3193/ryOIF0HcS)

## Notation
- サンプル空間: $\mathcal{X}$
- サンプル（ベクトル）: $\mathbf{x} \in \mathcal{X}$
    - ベクトルを表現する場合: $\mathbf{x} \in \mathbb{R}^{d}$
- 目標空間: $\mathcal{Y}$
- 目標値: $y \in \mathcal{Y}$
- 訓練事例集合: $D = \{ \left( \mathbf{x}^{(i)}, y^{(i)} \right) \in \mathcal{X} \times \mathcal{Y} | i \in 1, \ldots, n \}$
    - 集合に含まれるある1組のタプルを指して「サンプル」「データ点」と表現することもある

:::info
ベクトル・行列に対するインデクシングは代数の右下の添字として表現する。また、（訓練）事例集合の何番目のサンプルであるかは代数の右上の括弧付きの添字として表現する。また、ベクトルについては**太文字**で表現する（スカラーは普通のフォント）。

- e.g.
    - $x_{i}$ ← ベクトル$\mathbf{x}$の$i$次元目の要素
    - $\mathbf{x}^{(j)}$ ← 事例集合$X$の$j$番目のサンプル（のベクトル）
    - $x_{i}^{(j)}$ ← 事例集合$X$の$j$番目のサンプルの$i$次元目の要素
:::

## Framework
### 損失関数（Loss Function）
一つのデータ点（サンプル）について、予測値$\hat{y} \in \mathcal{Y}$と実測値$y \in \mathcal{Y}$の乖離の程度を表現する関数$l: \mathcal{Y} \times \mathcal{Y} \rightarrow \mathbb{R}$のこと。

#### 二乗誤差損失
もっとも基本的な損失関数であり、微分可能かつ凸関数であるため利用しやすい。実装がしやすいという利点の一方で、目標値の外れ値に対してロバストな推定が難しいという欠点がある。

$$
l_{\text{squared error}} \left( y, \hat{y} \right) = \left( y - \hat{y} \right)^{2}
$$

#### Huber損失
二乗誤差損失の欠点である外れ値に対するロバスト性を担保するための損失関数であり、実測値に対して予測値がどれほど離れていれば外れ値とみなすかを表現するハイパーパラメータ$\delta$が導入されている。

$$
l_{\text{Huber}} \left( y, \hat{y} \right) =
\begin{cases}
\frac{1}{2} \left( y - \hat{y} \right)^{2} & \text{if}\, |y - \hat{y}| \leq \delta \\
\delta \left( |y - \hat{y}| - \frac{1}{2}\delta \right) & \text{otherwise}
\end{cases}
$$

この関数は$y - \hat{y} = \delta$において微分不可能だが、連続である。

### 経験損失（Empirical Risk）
モデル$f: \mathcal{X} \rightarrow \mathcal{Y}$のパラメータを$\theta \in \Theta$とする。ある$\theta$についての訓練事例集合での経験的な損失の期待値を経験損失と言う。「経験的な」という言葉遣いは、実際に得られた（経験した）データの集合に対する損失であるということを強調するものと考えられる。

$$
J_{\text{emp}} \left( \theta | D \right) = \frac{1}{n} \sum_{ \left(\mathbf{x}, y \right) \in D } l \left( y, f\left( \mathbf{x}|\theta \right) \right)
$$

損失関数が与える評価値の平均値が経験損失の意味するところか。

### 汎化損失（Generalized Error, Expected Risk）
経験損失ではなく、訓練事例の**生成分布**$\rho$についてモデルの性能を評価してやりたい。

$$
J\left( \theta \right) = \int_{ \left( \mathbf{x}, y \right) \sim \rho } l \left( y, f\left( \mathbf{x} | \theta \right) \right) \text{d} \rho
$$

### 経験損失最小化（Empirical Risk Minimization: ERM）
経験損失を最小にするパラメータ$\theta^{\ast}$を、最良のものとして扱うパラメータ最適化のフレームワーク。サンプル数$n$が十分に大きいとき、経験損失最小化を経て得られるパラメータ$\theta^{\ast}$は、汎化損失を最小化するものに十分に近似されていることが知られている。

$$
\theta^{\ast} = \arg\min_{\theta \in \Theta} \frac{1}{n} \sum_{ \left( \mathbf{x}, y \right) \in D } l \left( y, f \left( \mathbf{x} | \theta \right) \right)
$$

### 正則化（Regularization）
モデルの複雑さを評価する関数$\text{Reg} = \Theta \rightarrow \mathbb{R}^{+}$を正則化項という。経験損失の式にこの正則化項を加えることを正則化と言う。

#### L1正則化
- 学習の結果得られるパラメータがスパースになる
- 一般線形モデルについて、損失関数にL1正則化を施したものはLASSO回帰と呼ばれる　

$$
\text{Reg}_{\text{L1}} \left( \theta \right) = \| \theta \|_{1}
$$

#### L2正則化
- パラメータ$\theta$について微分可能
- 影響度の大きい特徴量次元に対するパラメータの重要度を下げる
- 一般線形モデルについて、損失関数にL2正則化を施したものはRidge回帰と呼ばれる

$$
\text{Reg}_{\text{L2}} \left( \theta \right) = \| \theta \|_{2}
$$

### 正則化経験損失（Regularized Empirical Risk）
経験損失に正則化項を追加したもの。

$$
J_{\text{reg}} \left( \theta | D \right) = \frac{1}{n} \sum_{ \left(\mathbf{x}, y \right) \in D } l \left( y, f\left( \mathbf{x}|\theta \right) \right) + \frac{\lambda}{n}\text{Reg}\left( \theta \right)
$$

### 正則化経験損失最小化（Regularized ERM）
経験損失最小化の枠組みに、パラメータの正則化を施したもの。$\lambda$は正則化パラメータと呼ばれ、汎化性能に対する重み付けの役割を担う。

$$
\theta^{\ast} = \arg\min_{\theta \in \Theta} \frac{1}{n} \sum_{ \left( \mathbf{x}, y \right) \in D } l \left( y, f \left( \mathbf{x} | \theta \right) \right) + \frac{\lambda}{n} \text{Reg} \left( \theta \right)
$$

## Kernel Method
基底関数を$\phi: \mathbb{R}^{d} \rightarrow \mathbb{R}^{m}$とする。厳密には$d$次元の実ベクトルに対して定義する必要はないが、簡単のため表記を制限する。入力$\mathbf{x}$に対してこの基底関数による変換を噛ませることを**特徴抽出**といい、その変換結果を特徴ベクトルと見なせば、一般的な線形モデルと同じ枠組みで解釈することができる。

### カーネル関数（Kernel Function）
> カーネル関数は二つの入力$\mathbf{x} = \left( x_{1}, \ldots, x_{m} \right)^{T}$，$\mathbf{x}^{\prime} = \left( x_{1}^{\prime}, \ldots, x_{m}^{\prime} \right)^{T}$から計算される関数$k\left( \mathbf{x}, \mathbf{x}^{\prime} \right)$である。

> カーネル関数は特徴量で見たときの$\mathbf{x}$と$\mathbf{x}^{\prime}$の類似度を表していると考えることができる。

### リプレゼンター定理
損失関数に正則化を加えて最適化する問題において、正則化項が$\lambda\|\theta\|^{2}$という形をしていれば、最適解は$\mathbf{x}^{(i)} (i=1,2,\ldots,n)$をサンプル点として、

$$
f\left( \mathbf{x} \right) = \sum_{i=1}^{n} \alpha_{i} k\left( \mathbf{x}^{(i)}, \mathbf{x} \right)
$$

の形に書ける。

[カーネル法を利用した多変量回帰の導入](#カーネル法を利用した多変量回帰の導入)において、$\theta$を$\alpha$と$\phi\left(\mathbf{x}\right)$での表現に置き換えるときに、その置き換えの妥当性の根拠となる定理。

#### 行間を飛ばさない解釈
$\theta \in \mathbb{R}^{m}$をパラメータとし、特徴ベクトル$\phi\left( \mathbf{x} \right) \in \mathbb{R}^{m}$との内積を出力とするような次のモデル

$$
f\left( \mathbf{x} | \theta \right) = \sum_{i=1}^{m} \theta_{i} \phi_{i} \left( \mathbf{x} \right)
$$

を考えるとき、サンプル（サンプル点とラベル）に対する損失$l\left( y^{(i)}, f\left( \mathbf{x} | \theta \right) \right)$と正則化項を利用して損失が

$$
J\left( \theta | D \right) = \sum_{i=1}^{n} l\left( y^{(i)}, f\left( \mathbf{x}^{(i)} | \theta \right) \right) + \frac{\lambda}{n} \| \theta \|^{2}
$$

のように、表される場合については、**最適解が存在するのであれば、その解を利用したモデル式は**次のように表現できる。

$$
f\left( \mathbf{x} \right) = \sum_{i=1}^{n} \alpha_{i}k\left( \mathbf{x}^{(i)}, \mathbf{x} \right)
$$

つまり、**この損失を最小化するような最適解$\theta$は、サンプルの特徴ベクトルの線形和の形で表現できる**ということを主張している。

#### 証明
サンプルの特徴ベクトルの線形和を$\theta_{0} = \sum_{i=1}^{n} \alpha_{i} \phi\left( \mathbf{x}^{(i)} \right) \in \mathbb{R}^{m}$と置く。これにすべての$\phi \left( \mathbf{x}^{(i)} \right)$に直交する$\xi$という成分を加えた$\theta = \theta_{0} + \xi$という形で一般に$\theta$を表現することが可能である。

この$\theta$とサンプル$\mathbf{x}^{(j)}$の特徴ベクトル$\phi \left( \mathbf{x}^{(j)} \right)$との内積は$\phi \left( \mathbf{x}^{(j)} \right)^{T}\xi = 0$より

$$
f_{\theta} \left( \mathbf{x}^{(j)} \right) = \theta^{T}\phi \left( \mathbf{x}^{(j)} \right) = \theta_{0}^{T}\phi \left( \mathbf{x}^{(j)} \right)
$$

となり、$\xi$に依存しない。したがって、サンプル点だけの関数値$f_{\theta} \left( \mathbf{x}^{(j)} \right)$で決まる損失の値は$\xi$によらない。

正則化項については、$\theta_{0}$と$\xi$との直交性により

$$
\lambda \| \theta \|^{2} = \lambda \left( \| \theta_{0} \|^{2} + \| \xi \|^{2} \right)
$$

であるから、$\xi =  \mathbf{0}$のときに最小値を取る。

したがって、$\lambda \| \theta \|^{2}$を正則化項に持つ損失を最小とするような解は$\theta = \theta_{0}$となる。つまり、モデルは次のように表現される。

$$
\begin{eqnarray}
f \left( \mathbf{x} \right)
&=&
\sum_{i=1}^{n} \alpha_{i} \sum_{j=1}^{m} \phi_{j} \left( \mathbf{x}^{(i)} \right) \phi_{j}\left( \mathbf{x} \right) \\
&=&
\sum_{i=1}^{n} \alpha_{i} \phi\left(\mathbf{x}^{(i)}\right)^{T}\phi\left(\mathbf{x}\right)
\end{eqnarray}
$$

カーネル関数の定義$k\left( \mathbf{x^{(i)}}, \mathbf{x} \right) = \phi\left(\mathbf{x}^{(i)}\right)^{T}\phi\left(\mathbf{x}\right)$より、モデルは最終的に次のように表現できる。

$$
f\left(\mathbf{x}\right) = \sum_{i=1}^{n}\alpha_{i}k\left(\mathbf{x}^{(i)}, \mathbf{x}\right)
$$

##### 残課題
- 上で述べたような$\xi$は存在し得るのか？

#### @sfujiwara san
> 特定の損失関数にL2正則化項を足した最小化問題の解は、サンプルの線形和で表現できることを保証するやつ

> weightでサンプルの線形和が書けるので、weightとサンプルの内積はサンプルどうしの内積の線形和になって、カーネル法を適用できて嬉しい的な

### カーネル多変量解析（Kernel Multivariate Analysis）
#### カーネル法を利用した多変量回帰の導入
$$
\begin{eqnarray}
f \left( \mathbf{x} | \theta \right)
&=& \sum_{i=1}^{m} \theta_{i} \phi_{i}\left( \mathbf{x} \right) \\
&=& \sum_{i=1}^{n} \alpha_{i} \phi \left( \mathbf{x}^{(i)} \right)^{T} \phi \left( \mathbf{x} \right) \\
&=& \sum_{i=1}^{n} \alpha_{i} k\left( \mathbf{x}^{(i)}, \mathbf{x} \right)
\end{eqnarray}
$$

式を上から順番に説明する。

1. 最初の式はモデルのパラメータ$\theta$と特徴ベクトルの線形和をモデルの出力とすることを意味する。
    - 計算量に注目すると（$m$次元のベクトルどうしの内積計算のコストがかかるので）、$O\left(m\right)$となる。
2. パラメータ$\theta$の次元ではなく、$\theta$を得るための訓練事例集合について注目した場合の式に直す。**予測値を特徴ベクトルとパラメータの線形和ではなく、特徴ベクトルと訓練事例集合に含まれるサンプルの特徴ベクトルとの内積の重み（$\alpha_{i}$）付き和として表現し直している**。
    - 計算量に注目すると（$m$次元のベクトルどうしの内積計算をサンプル数$n$だけ繰り返しているので）、$O\left(mn\right)$となる。
3. $\phi\left(\mathbf{x}^{(i)}\right)$と$\phi\left(\mathbf{x}\right)$の内積$\phi\left(\mathbf{x}^{(i)}\right)^{T}\phi\left(\mathbf{x}\right)$をカーネル関数による表現に改めている。
    - $n$次元のベクトルどうしの内積計算のコストがかかるので、$O\left(n\right)$の計算量になる。
    - ただし、ここでは二つのベクトル$\mathbf{x}^{(i)}$と$\mathbf{x}$のカーネル関数の出力を得るための計算量について考慮していない。

$m \gg n$であるような問題設定を考えるとき、1番目と3番目では後者の方が計算量が小さく済むため、都合が良い。ただし、残課題として、カーネル関数の計算時に陽に内積計算をしてしまうと、結局2番目の式と状況が変わらない、というものがある。それを解決するための手法がカーネルトリックと呼ばれるもの。

#### カーネルトリック
カーネル関数の定義の二面性として、次がある。

1. 特徴ベクトルどうしの内積←これは[カーネル法を利用した多変量回帰の導入](#カーネル法を利用した多変量回帰の導入)の3番目の式
2. サンプルどうしの近さを表現するもの←類似度の表現としてのカーネル関数

##### 関数の半正定値性
> ある関数$k\left(\mathbf{x}, \mathbf{x}^{\prime}\right)$が正定値であるとは、任意の$n$個の点$\mathbf{x}_{1}, \ldots, \mathbf{x}_{n}$から計算される行列$K$を考えたとき、任意の$n$次元のベクトル$\alpha$について、その2次形式が常に非負であることが成り立つことである。

上記のような行列$K$はグラム行列（正定値行列とその随伴行列の積$A^{\ast}A$のこと）である。

###### なぜ正定値性の話が出てくるの？
:::success
基底関数$\phi$から抽出されるベクトルどうしの内積として表現されるカーネル関数$k\left(\mathbf{x}, \mathbf{x}^{\prime}\right) = \phi\left(\mathbf{x}\right)^{T}\phi\left(\mathbf{x}^{\prime}\right)$は、$\phi$を**適切に選ぶ**ことで、$k$が（半）正定値性を満たす。
逆に、ある関数$k$が正定値性を満たし、かつ入力である2つの実ベクトル$\mathbf{x}, \mathbf{x}^{\prime}$について対象である、つまり$k\left(\mathbf{x}, \mathbf{x}^{\prime}\right) = k\left(\mathbf{x}^{\prime}, \mathbf{x}\right)$であれば、（$\phi$を**適切に設定することにより**）その関数は何らかの2つのベクトルの内積として表現できる。

両方の側面からする「適切さ」を持つ関数こそがカーネル関数の候補であることが、正定値性について考える根源的な理由である。
:::

> カーネル関数を計算するのに、まず特徴ベクトルを計算し、その内積（成分の積和）を計算する必要があった。ところが、あらかじめ正定値性を満たすことがわかっている関数を持ってくれば、内積計算が不要となる。（中略）このように、正定値性をカーネル関数の定義とすれば、計算量をぐっと減らすことができる。これはまさに逆転の発送であり「カーネルトリック」と呼ばれている。

- $\phi$を設定したときに、その内積がカーネル関数と一致する形式で表現できる
- $\phi$を設定したときに、カーネル関数が正定値性を満たす

###### 具体例: ガウスカーネル
簡単のため、$x \in \mathbb{R}$を例として扱う。

- $\phi \left( x \right) = \{ a \exp \left( -\beta^{\prime} (z - x)^{2} \right) | z \in \mathbb{R} \}$: （$z$は実数なので）無限次元の特徴ベクトル
- $k \left( x, x^{\prime} \right)$は次の計算により、$k \left( x, x^{\prime} \right) = \exp \left( - \beta \| x - x^{\prime} \|^{2} \right)$と表現できる

$$
\begin{eqnarray}
k \left( x, x^{\prime} \right)
&=& \int_{-\infty}^{\infty} \phi_{z}\left( x \right)\phi_{z}\left( x^{\prime} \right)\text{d}z \\
&=& a^{2} \int_{-\infty}^{\infty} \exp\left( -\beta^{\prime} ( z - x )^{2} -\beta^{\prime} ( z - x^{\prime} )^{2} \right) \text{d}z
\end{eqnarray}
$$

###### 美味しさの秘訣
1. 実ベクトルについて特徴量の抽出を行う
2. 抽出された特徴ベクトルどうしの内積を計算する

という流れだったのが...

1. 実ベクトルについてのカーネル関数の出力を計算する

だけで済んでいる。

## Models
WIP

## Optimization
（正則化）経験損失最小化の枠組みにおけるモデルのパラメータの最適化手法についてまとめる。

#### 線形モデル
モデルが次のようなパラメータとの線形和で表現される場合を考える。ただし、モデルのパラメータ$\theta$は列ベクトルとする。

$$
f \left( \mathbf{x}|\theta \right) = \theta_{0} + \sum_{i = 1}^{d}\theta_{i}x_{i}
$$

この時の経験損失最小化の式は次のように表現できる。ただし、モデルの切片$\theta_{0}$について、$\mathbf{x}$の$0$次元めに$1$をパディングし、$n$個のデータについて束ねたものを行列$X$と表記している。また、各サンプルについての目標値$y$を列方向に束ねたものを$\mathbf{y} \in \mathcal{Y}^{n}$と表記する。

$$
\frac{1}{n}\left( \mathbf{y} - X\theta \right)^{T}\left( \mathbf{y} - X\theta \right) + \frac{\lambda}{n}\theta^{T}\theta
$$

==WIP==

## Appendix
### ガウス積分
$\beta \gt 0$なる定数とする時、次のような定積分の結果が知られている。Wikipediaによる解説は[こちら](https://ja.wikipedia.org/wiki/%E3%82%AC%E3%82%A6%E3%82%B9%E7%A9%8D%E5%88%86)。同一の被積分関数について、積分区間を設けない不定積分の結果は解析的に求めることができないことが知られている。

$$
\begin{eqnarray}
I
&=& \int_{- \infty}^{\infty} \exp \left( - \beta x^{2} \right) \text{d} x \\
&=& \sqrt{ \frac{ \pi }{ \beta } }
\end{eqnarray}
$$

#### 導出メモ
$$
\begin{eqnarray}
I
&=&
\int_{-\infty}^{\infty}\exp\left(-\beta x^{2}\right)\text{d}x \\
\frac{I}{2}
&=&
\int_{0}^{\infty}\exp\left(-\beta x^{2}\right)\text{d}x \\
\frac{I^{2}}{4}
&=&
\left(\int_{0}^{\infty}\exp\left(-\beta x^{2}\right)\text{d}x\right) \cdot \left(\int_{0}^{\infty}\exp\left(-\beta y^{2}\right)\text{d}y\right) \\
&=&
\left(\int_{0}^{\infty}\exp\left(-\beta x^{2}\right)\text{d}x\right) \cdot \left(\int_{0}^{\infty}\exp\left(-\beta x^{2}z^{2}\right)x\text{d}z\right) \\
&=&
\int_{0}^{\infty} \left( \int_{0}^{\infty} \exp \left( -\beta \left( 1 + z^{2} \right) x^{2} \right) x \text{d}x \right) \text{d}z \\
&=&
\int_{0}^{\infty} \left[ - \frac{1}{2 \beta \left( 1 + z^{2} \right)} \exp \left( - \beta \left( 1 + z^{2} \right) x^{2} \right) \right]_{0}^{\infty} \text{d}z \\
&=&
\frac{1}{2\beta}\int_{0}^{\infty}\frac{1}{1+z^{2}}\text{d}z \\
&=&
\frac{1}{2\beta}\left[ \arctan \left(z\right) \right]_{0}^{\infty} \\
&=&
\frac{\pi}{4\beta}
\end{eqnarray}
$$

| 行間 | 説明 |
|:----|:----|
| L1, L2 | 被積分関数が偶関数であることを利用している |
| L3, L4 | $y = xz$と置換する。$\text{d}y = x\text{d}z$であり、積分区間は変わらない |
| L4, L5 | $z$を定数として扱い、$x$について先に積分を解いている |
| L5, L6 | $\left( - \frac{1}{2\alpha} \exp\left( – \alpha x^{2} \right) \right)^{\prime} = \exp\left( -\alpha x^{2} \right) x$となることを利用している |
| L8, L9 | [arctan](#arctan)のグラフを参照 |

### arctan
![](https://i.imgur.com/2KIo3fu.jpg)

$x \rightarrow \infty$で$\frac{\pi}{2}$に収束する。

### Inverted Matrix
$A \in \mathbb{R}^{n \times n}$の平方行列とし、その$i, j$成分を$a_{i,j}$と表す。

余因子を利用する方法で逆行列を求める。余因子行列$\tilde{A}$は次のように定義される。

$$
\tilde{A}_{i,j} = \left(\begin{array}{cccccc}
{a_{1,1}} & {\cdots} & a_{1,j-1} & {a_{1,j+1}} & {\cdots} & {a_{1,n}} \\
{\vdots} & {\ddots} & {\vdots} & {\vdots} & {\ddots} & {\vdots} \\
a_{i-1,1} & {\cdots} & {a_{i-1,j-1}} & {a_{i-1,j+1}} & {\cdots} & {a_{i-1,n}} \\
{\vdots} & {\ddots} & {\vdots} & {\vdots} & {\ddots} & {\vdots} \\
a_{n,1} & {\cdots} & {a_{n,j-1}} & {a_{n,j+1}} & {\cdots} & {a_{n,n}} \\
\end{array}\right)
$$

もとの行列に対して、$i$行と$j$列を除いて定義される$\mathbb{R}^{n-1 \times n-1}$の行列と言える。この時、行列$A$の余因子$\Delta_{i,j}$はこの余因子行列$\tilde{A}$を利用して次のように定義される。

$$
\Delta_{i,j} = \left(-1\right)^{i+j} \det \tilde{A}_{i,j}
$$

上式における$\det$は行列式を意味する。

#### 行列式の計算
$A \in \mathbb{R}^{n \times n}$について考える。$n$個の成分の順列は$n!$個考えることができ、それら順列をまとめたものを$S_{n}$とする（要素数は$n!$個）。$\sigma \in S_{n}$なる順列が偶置換であるとき$1$を、奇置換であるとき$-1$を返す関数とする。このとき、行列式は次のように計算できる。ただし、$\sigma\left(i\right)$は順列の$i$番目の要素とする。

$$
\det A = \sum_{\sigma \in S_{n}} \text{sgn}\left(\sigma\right)\prod_{i=1}^{n}a_{i, \sigma\left(i\right)}
$$

#### 転倒数（反転数）
英語だと`inversion`と呼ばれる。$O\left(n \log n\right)$の計算量で求めることが可能。

##### 参考
- [はてなブログ](https://kira000.hatenadiary.jp/entry/2019/02/23/053917)
- [Qiita](https://qiita.com/wisteria0410ss/items/296e0daa9e967ca71ed6)

#### 具体例
以下のような行列$A \in \mathbb{R}^{3 \times 3}$の逆行列を求める。
$$
A = \left(\begin{array}{ccc}
{1} & {1} & {-1} \\
{-2} & {0} & {1} \\
{0} & {2} & {1}
\end{array}\right)
$$

```python=
import numpy as np

A = np.array([
    [ 1,  1, -1],
    [-2,  0,  1],
    [ 0,  2,  1],
], dtype=np.float64)

# n=3なので、順列の集合S3は...
# (1, 2, 3), (1, 3, 2)
# (2, 1, 3), (2, 3, 1)
# (3, 1, 2), (3, 2, 1)
# の6通りを考えることができる。
# また、それぞれの順列が偶置換であれば+1, 奇置換であれば-1
# となる符号の置換列Sgnは...
# +1, -1
# -1, +1
# +1, -1
# となる

S3 = np.array([
    (1, 2, 3), (1, 3, 2),
    (2, 1, 3), (2, 3, 1),
    (3, 1, 2), (3, 2, 1),
], dtype=np.int32)

Sgn = [1, -1, -1, 1, 1, -1]

det = 0

for sigma, sgn in zip(S3, Sgn):
    e = 1
    for i, j in zip(range(3), sigma):
        j = j - 1  # インデックスなので1引いておく
        e *= A[i,j]
    det += sgn * e

# >>> print(det)
# 4.0
```

##### 実装アイディア
- 余因子行列のジェネレータ
- 行列式を求める関数
    - $n$の順列と置換の種類を返す関数
        - 順列を生成する関数と、その置換の判別を行う関数が定義できれば求められる
