畳み込み

1.畳み込みとは
 畳み込み(たたみこみ、英: Convolution)とは、2つの関数を合わせて新しい関数を作る数学的な操作です。主に信号処理や画像処理、機械学習(特に畳み込みニューラルネットワーク、CNN)で広く使用されます。
 畳み込みの基本的な概念を理解するために、1次元の畳み込みについて説明します。2つの関数fとgの連続的な畳み込み(畳み込み積分)は、以下のように定義されます。

2.連続的な畳み込み(畳み込み積分)
 畳み込みは2つの関数を操作して新しい関数を生成する数学的な操作です。

畳み込みを視覚的に理解するために、例として、次の関数の畳み込みを考えます。

3.離散的な畳み込み(畳み込み和)

y=[0. 1. 2.5 4. 5.5 7. 2.5]

単純パーセプトロン

1.概要
 単純パーセプトロンは、最も基本的な形式のニューラルネットワークで、1つのニューロンから構成されます。入力ベクトルと重みベクトルの内積にバイアスを加え、その結果を活性化関数(しきい値関数)によって出力します。線形分離可能な問題に対して適していますが、非線形問題には対応できません。
 すなわち、入力データが線形平面で2つのクラスに分けられる場合にのみ分類が可能となり、非線形な問題や複雑なクラスの境界線を持つ問題には対応できません。多クラス分類問題に対応するには、複数の単純パーセプトロンを組み合わせたり、他の手法(例えば多クラスロジスティック回帰や多層パーセプトロン)を使用する必要があります。
 雑なモデルや手法を検討する必要があります。

2.パーセプトロンの構造と動作
 パーセプトロンは、ニューラルネットワークの最も基本的な単位であり、単層の線形分類器です。1950年代から1960年代にかけて、フランク・ローゼンブラットによって考案されました。パーセプトロンは、与えられた入力データを2つのクラスに分類するために使用されます。
(1)パーセプトロンの構造と動作
 パーセプトロンの基本的な構造は次の通りです。
 ①入力層
  特徴量ベクトルx=[x1,x2,・・・,xn]を入力します。各特徴量は重み付けされます。
 ②重みとバイアス
  各入力特徴量には対応する重み w=[w1,w2,・・・,wn]があります。
  バイアスb は、入力に対する追加の定数項です。
 ③線形結合
  入力特徴量と重みのドット積にバイアスを加えて、線形出力zを計算します。
  z = w・x+b ”・”はベクトル積
 ④活性化関数
  線形出力z に対して活性化関数を適用し、最終的な出力yを得ます。
  パーセプトロンでは、ステップ関数(Heavisideステップ関数)が一般的に使用されます。

(2)パーセプトロンの学習
 パーセプトロンは、誤差修正アルゴリズム(パーセプトロン学習アルゴリズム)を使用して重みとバイアスを更新します。学習の手順は以下の通りです。
 ①初期化:
  重みベクトルw とバイアスb を小さなランダムな値で初期化します。
 ②各トレーニングデータポイントの処理:
  各トレーニングデータ(x,y_true)に対して、以下を行います
 ・線形出力zを計算します
  z = w・x+b ”・”はベクトル積
 ・活性化関数を適用して予測ypredを得ます
 ・誤差を計算します
  error=ytrue-ypred
 ・重みとバイアスを更新します

③エポック
 上記の処理を全てのトレーニングデータに対して行い、これを複数回(エポック)繰り返します。

3.単純パーセプトロンのサンプル

 トレーニングデータ、テストデータ、及びepoch毎の実行結果weights、biasの線形出力をグラフに表示します。
 ここで、トレーニングデータのラベル0:青×、ラベル1:青○、テストデータのラベル0:赤×、ラベル1:赤○とします。 

4.プログラムの解説
(1)SimplePerceptron クラスの初期化

・特徴量の数 num_featuresを設定し、重みベクトルの初期化を行います。
 self.num_features: 2
 self.weights: [0. 0.]
・学習率 learning_rate、エポック数 epochs を設定します。

(2)predict メソッド(予測値を算出)

入力ベクトル(特徴量) x を用いて予測を行います。線形結合の結果を活性化関数として使い、閾値に基づいて予測を二値化します。
・linear_output = np.dot(self.weights, x) + self.bias
 重みと特徴量のベクトル積にバイアス値を加算(線形結合) 
・prediction = 1 if linear_output > 0 else 0
 三項演算子を使用しており、linear_output が 0 より大きい場合は 1 を、そうでない場合は 0 を prediction に代入します。

(3)train()メソッド(重み、バイアスの更新)

・与えられた訓練データ X_train(特徴量ベクトル) と y_train(ラベル値) を用いてパーセプトロンを学習します。各訓練データに対して予測を行い、誤差を計算し重みとバイアスを更新します。これをエポック数繰り返します。
・for x, y_true in zip(X_train, y_train):
X_train, y_train リストの要素を取り出す

(4)evaluate()メソッド(評価)

・train()メソッドで算出した、weights(重みベクトル)、bias(バイアス値)を使って、予測値を算出し、ラベルの値と一致するか否かの評価を行う。

(5)main

・if name == “main
  スクリプトが直接実行されたときにのみ、実行するための条件文
 ・np.array()でndarrayオブジェクトに変換する
 ・SimplePerceptron(num_features=2)でインスタンスを生成
 ・train()メソッドで重みベクトル、バイアス値の更新
 ・evaluate()で評価する

以上

各プログラミング言語の特徴

目次

1.言語の仕様
2.変数の型
3.プログラム構文の違い
4.命令セットとコンパイラ、プログラミング言語の関係
5.各プログラミング言語のプラットフォームへの依存性
6.各プログラミング言語による実装
7.GUI

以下の各プログラミング言語について、
 ・言語の仕様
 ・変数の型 
 ・プログラム構文の違い
 ・命令セットとコンパイラ、プログラミング言語の関係
 ・各プログラミング言語のプラットフォームへの依存性
 の各項目についてまとめました。

(比較したプログラミング言語)
 ①C、C++
 ②Java
 ③C#
 ④Python

1.言語の仕様
①CとC++
 C17  C言語の標準規格ISO/IEC 9899:2018
 C++20 C++ の標準規格 ISO/IEC 14882:2020
②java
 主にJava Community Process (JCP) によって決定される。JCP は、Java言語仕様、Java仮想マシン (JVM) 仕様、Java Standard Edition (Java SE) APIなどのJava標準の開発を監督するコミュニティ主導の組織
 仕様は、JSRs(Java Specification Requests)に記述されています
(参考) 
 List of all JSRs
 https://www.jcp.org/en/jsr/all

③C#
ECMA-334(Ecma International、以前の欧州電子計算機工業会)
ISO/IEC 23270
JIS X 3015
(参考)
 バージョン7.0
  ECMA-334:2023 (2023年12月)
  ISO/IEC 20619:2023 (2023年9月)
  リリース時期2017年3月 .NET Framework 4.7 Visual Studio2017 version 15.0
  https://ja.wikipedia.org/wiki/C_Sharp

④Python
 Pythonソフトウェア財団(英: Python Software Foundation; PSF)がサポート
 Python Enhancement Proposals (PEP): PEP は、Python の新機能、改善、または変更を提案を行うための文書
(参考)
 Python Software Foundation; PSF
 https://www.python.org/psf-landing/

2.変数の型
 変数はデータを格納する領域、型は格納できるデータの種類をコンパイラやインタープリタに知らせる役割を持ち、バイト長、領域の扱い方などが決められています。
 ここでは、オブジェクトの概念ができる前からある基本データ型とオブジェクトの概念に基づく参照型の分け方で整理します。
①C言語
(1)変数の主な型の一覧

(注1)型で使用できる範囲はコンピュータによるが32bitマシンの例を示す

    サイズはsizeof演算子で調べることができる (例)sizeof ( char )

(2)記憶クラス
 C 言語の変数はその宣言の場所と方法によって、その変数の通用範囲とメモリの割り当て方法(自動記憶域のスタック領域と静的記憶域のスタティック領域)が決まります。
 代表的な変数の性質(記憶クラス)には以下のものがあります。

①’ C++
 変数の型に関するCからの主な変更点を下記に示します。
・bool 型の変数には値 true と falseを設定できる。Cの_BOOLでは、値は通常、整数(偽の場合は 0、真の場合は非ゼロ) を使用。
・文字列型の追加。Cでは通常、文字列は文字の配列(char*char[])を使用。
・クラス型とオブジェクト指向機能のサポートにより、ユーザー定義型の作成が可能となった。
・テンプレートが導入され、任意のデータ型を処理できる関数とクラスが作成できる。
・参照型が追加され、C のポインターを使った関数の引数の操作に代わる方法を提供。
・列挙型クラスの作成ができる
(1)C++言語の変数の主な型の一

(注1)型で使用できる範囲はコンピュータによるが32bitマシンの例を示す
サイズはsizeof演算子で調べることができる (例)sizeof ( char )

(2)記憶クラス
 (C 言語と同じ)
new演算子で作成する配列、クラスのオブジェクトはヒープ領域上に割り当てられま
 す。そのオブジェクトへのポインタ自体は他の変数と同様に記憶クラスに従ってメモリに保存されます。

(参考) 
・[C++] new演算子による配列のメモリの動的確保
https://qiita.com/toshi_machine/items/c0682b7ae6c951fcadf8

・C++の基礎 : new/delete 演算子
http://www.s-cradle.com/developer/sophiaframework/tutorial/Cpp/newdelete.html

②java
(1)java言語の変数の主な型の一覧

(2)記憶クラス
 javaは、ガベージコレクションによりメモリを自動的に管理するため、Cの場合のようにメモリを明示的に割り当てたり割り当て解除したりする必要ありません。java では、new演算子でオブジェクトを作成すると、メモリがヒープ上に自動的に割り当てられます。オブジェクトが参照されなくなると、ガベージ コレクターによって自動的に割り当てが解除されます。

③C#
(1)C#言語の変数の主な型の一覧

(2)記憶クラス
 ガベージコレクションによりメモリを自動的に管理するため、C の場合のようにメモリを明示的に割り当てたり割り当て解除したりする必要はありません。C#では、new演算子でオブジェクトを作成すると、メモリがヒープ上に自動的に割り当てられます。オブジェクトが参照されなくなると、ガベージ コレクターによって自動的に割り当てが解除されます。

④Python
(1)Python言語の変数の主な型の一覧

・データ型を調べる時は、type()を使用
・Pythonは「動的型付き言語」で変数を作成するときは型の限定までは行わず、実行時にデータ型が判定される
・各データ型にはそれぞれ固有のメソッドがある

(2)記憶クラス
  メモリがヒープ上に自動的に割り当てられ、ヒープの管理は Python メモリマネージャ (Python memory manager)が行う

(参考)
Python/C API リファレンスマニュアル>>メモリ管理
https://docs.python.org/ja/3/c-api/memory.html

3.プログラム構文の違い
 変数の書き方など、ソースファイルを作成するときのスタイルについて、各プログラミング言語でまとめました。

表.各プログラミング言語とコーディングスタイルの一覧

(注1)プログラムのフォーマット
 空白 (スペース、タブ、改行) がプログラムの動作に影響を与えないものをフリーフォーマットという。特に「インデントの入れ方」と「中括弧{}の位置」については、可読性を高めるためのスタイル例を下記に示す。
・インデントは空白文字4文字を基本とする
・中括弧の位置はK&Rスタイルに準じる。
各言語のコーディングスタイルに準じるようにする。

UpperCamelCase:単語の先頭を大文字にする(PascalCase)
 lowerCamelCase:先頭は小文字、以降の単語の先頭を大文字にする
  単語の先頭を大文字にするのは、単語間の区切りを表現するためで、
  最初の単語も大文字にするのがパスカルケース(アッパーキャメルケース)、
  最初の単語は小文字にするのがローワーキャメルケース
 snake_case:全て小文字としてアンダースコアで連結する

4.命令セットとコンパイラ、プログラミング言語の関係

5.各プログラミング言語のプラットフォームへの依存性
表.各プログラミング言語のプラットフォーム(動作環境のこと)への依存性

6.各プログラミング言語による実装
 ソフトウェア開発を要件定義、外部設計、および内部設計、実装(コーディング)、テストと分けたとき、内部設計までの各フェーズでは主に問題の理解、システムアーキテクチャの定義、ソフトウェアの機能、外部システム及びユーザーとのインタフェースを設計することに重点が置かれ、プログラミング言語を前提にしないことが重要と考えます。
 実装のフェーズでは、設計仕様を実際のコードに変換する必要があり、プロジェクトの要件、パフォーマンス、既存のシステムとの互換性、開発チームのスキルセットなどを考慮して適切なプログラミング言語を選択することになります。

7.GUI
 GUIについて、プログラミング言語、及びOS(windows、Linux)で使われているツールについて

(1)Qt
 Qt はクロスプラットフォーム環境で使用でき、主に C++開発で使用されます。
 C#でもQt for C# (QtSharp)を使用することでクロスプラットフォーム GUI アプリケーションを構築できます。

(2)WPF(Windows Presentation Foundation)
 WindowsでのC#開発で使用。WPFはマイクロソフト社が開発する.NET Framework3.0以降に含まれるユーザーインターフェースサブシステムのこと。

(3)GTK
 GTK+(The Gimp Toolkit+)はC言語用のGUIツールキット、Gtk# (GtkSharp) は、GNOME などの Linux デスクトップ環境で使用される一般的なオープンソース GUI ツールキットである GTK+ ツールキットの .NET バインディング。

C言語のメモリ環境について

目次
1.メモリ領域の違い
1.1 各メモリの割り当て状況を確認する
2.スタックメモリの確認方法
3.ヒープメモリの確認
(1)動的メモリのデバッグツール:Valgrind(ヴァルグリンド、GNU General Public License)
(2)プロセスのメモリマッピングの確認
(3)/procファイルシステムの使用
(4)ランタイムライブラリの関数
4.Linuxのメモリマップ設定

1.メモリ領域の違い

メモリ領域          特徴
スタック領域・関数呼び出しやローカル変数の割り当てに使われる。・後入れ先出し(LIFO)のデータ構造をもつ。
ヒープ領域・プログラマが明示的にメモリの確保(C:malloc 関数、C++:new演算子)と解放(freeなど)を行う必要がある。・動的にメモリを確保・開放するのに適したデータ構造をもつ。
静的領域          ・グローバル変数などの静的変数が置かれる。
テキスト領域・機械語に翻訳されたプログラムが格納され、この機械語の命令が1行づつ実行されることでプログラムが動く。

(参考)
・メモリの4領域
https://brain.cc.kogakuin.ac.jp/~kanamaru/lecture/MP/final/part06/node8.html

1.1 各メモリの割り当て状況を確認する
(1)テキスト領域
 テキスト領域(プログラムコードが格納される領域)は、通常、実行ファイルの読み込みアドレスを確認することで確認できます。以下は、テキスト領域の開始アドレスを取得する方法です。

ここで、”(void)main”の “(void)”はvoidは、どんなデータ型にもキャスト可能なポインタ型です。これは、特定のデータ型を指さない汎用ポインタであり、さまざまな型のデータを指すことができます。voidは型情報を持たないため、キャストを間違えると意図しない動作を引き起こす可能性があり、ポインタ演算など行うときは正しいデータ型にキャストするよう注意が必要です。

(2)静的領域
 静的領域(グローバル変数や静的変数が格納される領域)のアドレスを取得するには、静的変数やグローバル変数のアドレスを確認します。

(3)スタック領域
 スタック領域のメモリ割り当ては、ローカル変数のアドレスを確認することで確認できます。

(4)ヒープ領域
 ヒープ領域のメモリ割り当ては、malloc関数で動的に割り当てたメモリのアドレスを確認することで確認できます。

各種類の領域をまとめて表示

2.スタックメモリの確認方法
(1)GDB (GNU Debugger)の使用

(参考)
・gdbのコマンド一覧
https://www.fos.kuis.kyoto-u.ac.jp/le2soft/siryo-html/node49.html

gdbの主なコマンド

コマンド名省略型動作
run [args]rプログラムを(引数argsで)実行する
break nb nブレークポイントの設定、n行目又は関数名
deleated nnのブレークポイントを削除
info breakpointsi bブレークポイントの一覧
continuecプログラムの実行を再開
stepsステップ・イン実行
backtrace btバックトレースの表示
quitqgdbの終了

(2)プロセスリミットの確認
 Unix系OSでは、ulimitコマンドでプロセスのスタックサイズリミットを確認できます。
 ulimit -s コマンドを実行して 8192 と表示される場合、それはスタックサイズの制限をキロバイト単位で表しています。具体的には、現在のシェルセッションにおけるスタックサイズの制限が 8192 キロバイト(8 メガバイト)であることを示します。
 スタックサイズが小さいと、深い再帰や大量のローカル変数を使用するプログラムはスタックオーバーフローを引き起こす可能性があります。

 スタックサイズは次のシェルを実行することで16メガバイトにできます。
 $ ulimit -s 16384
 システム上で永続的に設定するときは、システムの設定ファイル(例えば /etc/security/limits.conf)を変更する必要があります(この設定ファイルには該当箇所はありませんでした)。

3.ヒープメモリの確認
(1)動的メモリのデバッグツール:Valgrind(ヴァルグリンド、GNU General Public License)
 Valgrindは、メモリリークや不正なメモリアクセスを検出するための動的解析ツールの一つです。Valgrindを使用してプログラムを実行すると、ヒープメモリの使用状況が詳細に報告されます。

①Valgrindの概要
 Memcheck:Valgrindの一部で、メモリエラーの検出を行います。
 Copyright:Valgrindの著作権情報。
 Version:使用しているValgrindのバージョン。
 Command:実行されたコマンド(ここでは ./a.out )。
②プログラムの出力
 プログラム自体の出力でfunc2関数からの出力。
③HEAP SUMMARY(ヒープの概要)
・in use at exit:プログラム終了時に使用中のメモリの量。ここでは、0バイトが0ブロックに使用されていることを示しています。つまり、全てのメモリが解放されています。
・total heap usage:プログラムの実行中に行われたメモリ割り当て(allocs)と解放(frees)の総数、および割り当てられたメモリの総量。
・1 allocs:1回のメモリ割り当てが行われた。
・1 frees:1回のメモリ解放が行われた。
・1,024 bytes allocated:合計で1,024バイトのメモリが割り当てられた。
④メモリリークの確認
・All heap blocks were freed:すべてのヒープメモリブロックが解放されたことを示します。
・no leaks are possible:メモリリークがないことを意味します。
⑤エラーの概要
・0 errors from 0 contexts:検出されたエラーはゼロであることを示します。
・suppressed: 0 from 0:抑制されたエラーはないことを示します。

 このValgrindの出力から、次のことがわかります:
・プログラムは正常に実行され、出力も予期されたものでした。
・メモリ使用に関しては、メモリリークは発生していません。
・割り当てられたメモリはすべて適切に解放されています。
・検出されたメモリエラーはありません。
 この結果から、プログラムはメモリ管理に関して健全であると言えます。Valgrindを使用することで、メモリリークやその他のメモリエラーを検出し、修正することができます。

(参考)
・Valgrind による動的解析
https://qiita.com/QGv/items/d7857cb0fff80ecbe41d#valgrind%E3%81%A8%E3%81%AF

(2)プロセスのメモリマッピングの確認:
 Linuxでは、pmapコマンドを使ってプロセスのメモリマッピングを確認できます。ヒープ領域のサイズと位置を特定することができます。
メモリを動的に割り当てる簡単なCプログラムを用意します。

ヒープ領域の詳細な確認
詳細なメモリ使用情報を表示するために、pmapコマンドに-xオプションを使用

出力の中に、[ heap ] というラベルは表示されない場合、[ anon ] として表示される行がヒープ領域を示すことが多いとのこと。
 上記の例では、
 00007fe5066f0000 10244 10244 10244 rw— [ anon ]
アドレスにある 10MB(10244KB)の匿名メモリがヒープ領域です。

(3)/procファイルシステムの使用:
 /proc/[pid]/maps:
 Linuxでは、/procファイルシステムを使ってプロセスのメモリマップを確認できます。ヒープ領域は通常、heapとして表示されます

(4)ランタイムライブラリの関数:mallinfo:
 mallinfo関数は、ヒープメモリの統計情報を提供します。例えば、以下のコードでヒープメモリの使用状況を表示できます。

実行結果

c_heaptestを実行し、入力待ちになっている状態で、mallinfo()を実行

4.Linuxのメモリマップ設定
 Linuxでは、スタックのサイズはulimitコマンドで確認および設定できます

・標準のリンカスクリプト
 GCCがデフォルトで使用するリンカスクリプトは、インストールされたツールチェーンのディレクトリにあります。標準スクリプトの場所を見つけるには、以下のコマンドを使用して標準のリンカスクリプトを表示することができます

実際のプログラムをコンパイルしたとき、どのようにメモリに配置されるか確認します。

ここで、
・VMA(Virtual Memory Address)
 VMAは、セクションが実行時に仮想メモリ空間内で占めるアドレスを示します。プログラムが実行されるときに、CPUがこのアドレスを使用してメモリにアクセスします。
例えば、コードセクション(.text)やデータセクション(.data)の実行時のアドレスはVMAで決まります。VMAは、プログラムが実行されるときに、オペレーティングシステムが各セクションをマッピングするアドレスです。
・LMA(Load Memory Address)
 LMAは、セクションが実際にメモリにロードされるアドレスを示します。特に、初期化データセクション(.data)やBSSセクション(.bss)のようなセクションで重要です。
LMAは、プログラムがディスクからメモリにロードされるときに、セクションが配置される物理メモリのアドレスです。
・VMAとLMAの違い
 VMAは実行時のアドレス、LMAはロード時のアドレスです。
 通常、コードセクション(.text)や読み取り専用データセクション(.rodata)では、VMAとLMAが同じです。しかし、初期化データセクション(.data)や未初期化データセクション(.bss)では異なることがあります。例えば、.dataセクションはROMからRAMにコピーされる場合、ROM上のアドレスがLMAであり、RAM上のアドレスがVMAです。

(参考)
・objdump – オブジェクトファイルの情報を表示する
https://linuxcommand.net/objdump/#_-h

(2)組み込みシステムやカスタムOSでのメモリマップ設定
 組み込みシステムやカスタムOSの場合、メモリマップはリンクスクリプト(例えば、GNU linkerの.ldファイル)によって設定されます。
・MEMORYセクション:使用可能なメモリ領域を定義します。
 各メモリ領域の開始アドレス(ORIGIN)とサイズ(LENGTH)を指定します。
・SECTIONSセクション:セクションの配置を定義します。
 各セクションがどのメモリ領域に配置されるかを指定します。

(参考)
・【 ulimit 】コマンド――ユーザーが使用できるリソースを制限する
https://atmarkit.itmedia.co.jp/ait/articles/1908/01/news031.html

・GNU Cを使いこなそう2017/06/30
リンカスクリプトを理解しよう
https://www.computex.co.jp/article/use_gcc_1.htm

AIモデル作成で使用するPythonのメソッド

目次
1.NumPyのメソッド
(1)np.array()
(2)np.zeros()
2.ndarray(N-dimensional arrayの略)
(1)ndarray.shape
(2)ndarray.dtype
(3)ndarray.reshape()
(4)ndarray.flatten()
(5)ndarray.sum()
(6)ndarray.mean()
(7)ndarray.max() と ndarray.min()
(8)ndarray.dot()
(9) ndarray.transpose()
3.ベクトルの類似性の評価
3.1 内積
(1)行ベクトル間の内積
(2)列ベクトル間の内積
3.2 コサイン類似度
4.コレクションデータ型とメソッド
4.1 リスト (List)
4.2 タプル (Tuple)
4.3 セット (Set)
4.4 ディクショナリ (Dictionary)
4.5 デフォルトディクショナリ (defaultdict)
5.データ構造の操作
5.1 インデックス参照(スライス)
(1)リストのスライス
(2)文字列のスライス
(3)1次元配列のスライス
(4)2次元配列のスライス
(5)3次元配列のスライス
(6)スライスオブジェクト

1.NumPyのメソッド
(1)np.array()
 多次元配列を作成します。

(2)np.zeros()
 全ての要素がゼロで初期化された新しい配列を生成します。この関数は、配列の形状とデータ型を指定して、必要なサイズのゼロ配列を作成します。
a)1次元配列の生成

b)2次元配列の生成

c)データ型を指定する
 dtype 引数を使用して配列のデータ型を指定できます。デフォルトでは、float64 型になりますが、整数型や他の型を指定することもできます。

(3)np.pad()
 np.pad()は、NumPy配列にパディング(追加の要素)を付けるための関数です。パディングは、配列の周りに一定の値や特定の方法で追加される要素です。これにより、配列のサイズを増やすことができます。

基本的な構文
numpy.pad(array, pad_width, mode, **kwargs)
ここで、
 ・array:配列
・pad_width:各軸ごとのパディングする量。
  pad_width=[((before_1, after_1), … (before_N, after_N))]
(before_i, after_i) は axis=i の index=0 側に before_i、index=-1 側
に after_i だけパディングすることを意味します
・mode:constant 標準、edge 端の値をコピーなど
・**kwargs:オプション

2.ndarray(N-dimensional arrayの略)
 NumPyではPythonのリストではなく、効率性などからndarrayという独自のデータ構造を演算に使う。
 同じ属性や大きさを持った要素を持つ多次元配列を扱うためのクラスの1つ
 NumPy では Python の標準機能であるリスト型変数ではなく、NumPyのndarray型という特別な配列を使用し、これにより行列の掛け算、逆行列、固有値などの計算を高速に行うことができる。
ndarrayが持つ属性
 (ndarrayのインスタンス変数名).(属性)でndarrayインスタンスの情報を取得することができる。
 ndarrayクラスには、データ操作や計算に役立つ多くのメソッドが用意されています。以下に、いくつかの主要なメソッドを紹介します。
属性
ndarray.shape: 配列の形状を表すタプル。
ndarray.dtype: 配列の要素のデータ型。
ndarray.size: 配列の要素の総数。
ndarray.ndim: 配列の次元数。
メソッド
ndarray.astype(dtype): 配列の要素のデータ型を指定された型に変換した新しい配列を返す。
ndarray.copy(): 配列のコピーを作成して返す。
ndarray.flatten(): 配列を1次元にフラット化した新しい配列を返す。
ndarray.reshape(shape): 配列の形状を変更した新しい配列を返す。
ndarray.transpose(*axes): 配列の軸を並べ替えた新しい配列を返す。
ndarray.sum(axis=None): 配列要素の合計を返す。
ndarray.mean(axis=None): 配列要素の平均を返す。
ndarray.min(axis=None): 配列要素の最小値を返す。
ndarray.max(axis=None): 配列要素の最大値を返す。
ndarray.argmin(axis=None): 配列要素の最小値のインデックスを返す。
ndarray.argmax(axis=None): 配列要素の最大値のインデックスを返す。
ndarray.cumsum(axis=None): 配列要素の累積和を返す。
ndarray.cumprod(axis=None): 配列要素の累積積を返す。

(1)ndarray.shape
 NumPyの配列(ndarray)オブジェクトの形状を取得します。各次元のサイズを表すタプルが返されます。

(2)ndarray.dtype
 配列のデータ型を取得します。

(3)ndarray.reshape()
 配列の形状を変更します。元のデータは保持されますが、形状が変わります。
 (元の配列)
 array = np.array([1, 2, 3, 4, 5, 6])
 (変更後の配列)
 reshaped_array [[1 2 3]
 [4 5 6]]

(4)ndarray.flatten()
 多次元配列を1次元に変換します。

(5)ndarray.sum()
 配列の全要素の合計を計算します。軸を指定することで、その軸に沿った合計も求められます。

(6)ndarray.mean()
 配列の全要素の平均を計算します。軸を指定することで、その軸に沿った平均も求められます。

(7)ndarray.max() と ndarray.min()
 配列の最大値と最小値を取得します。軸を指定することで、その軸に沿った最大値と最小値も求められます。

(8)ndarray.dot()
 行列のドット積(内積)を計算します。

(9) ndarray.transpose()
 配列の転置を返します。行と列を入れ替えます。

3.ベクトルの類似性の評価
3.1 内積
(1)行ベクトル間の内積
 行列aと行列bの各行ベクトル間の内積を計算する方法を示します。

(2)列ベクトル間の内積
 行列aと行列bの各行ベクトル間の内積を計算する方法を示します。

3.2 コサイン類似度

ベクトルのノルム(ユークリッドノルム)
デフォルトでは、np.linalg.norm() はユークリッドノルム(L2ノルム)を計算します。

4.コレクションデータ型とメソッド
4.1 リスト (List)
 リストは、順序付けられた変更可能なコレクションです。

4.2 タプル (Tuple)
 タプルは、順序付けられた変更不可能なコレクションです。

4.3 セット (Set)
 集合セットは重複しない要素(同じ値ではない要素、ユニークな要素)のコレクションで、和集合・差集合・積集合などの集合演算を行うことができます。

Union: {1, 2, 3, 4, 5, 6, 7, 8}
Union: {1, 2, 3, 4, 5, 6, 7, 8}
Difference (set1 – set2): {1, 2, 3}
Difference (set1 – set2): {1, 2, 3}
Difference (set2 – set1): {8, 6, 7}
Difference (set2 – set1): {8, 6, 7}
Intersection: {4, 5}
Intersection: {4, 5}

4.4 ディクショナリ (Dictionary)
 ディクショナリは、キーと値のペアのコレクションです。順序はPython 3.7以降で保持されます。

{‘a’: 10, ‘b’: 2, ‘c’: 3, ‘d’: 4}
{‘a’: 10, ‘c’: 3, ‘d’: 4}
dict_keys([‘a’, ‘c’, ‘d’])
dict_values([10, 3, 4])
dict_items([(‘a’, 10), (‘c’, 3), (‘d’, 4)])
10

4.5 デフォルトディクショナリ (defaultdict)
 defaultdict は、キーが存在しない場合にデフォルト値を提供するディクショナリです。
 このデフォルト値を生成するために使用されるのがファクトリ関数です。
 defaultdict を使用するには、collections モジュールをインポートし、デフォルト値を生成するためのファクトリ関数を渡してインスタンスを作成します。

 ファクトリ関数として、いくつかの組み込み関数や独自に定義した関数を使用できます。
 ・int:デフォルト値として0を返す
 ・list:デフォルト値として空のリストを返す
 ・set:デフォルト値として空のセットを返す
defaultdict(, {‘a’: 1})
defaultdict(, {‘a’: [1]})
defaultdict(, {‘a’: {1}})

4.6 デキュー (deque)
 deque は、両端での高速な挿入と削除が可能なデータ構造です。

deque([0, 1, 2, 3, 4, 5, 6])
deque([1, 2, 3, 4, 5])

5.データ構造の操作
5.1 インデックス参照(スライス)
 リスト、タプル、文字列、辞書、セット、Numpy配列などのデータ構造を操作するための基本的な方法です。これにより、特定の要素へのアクセス、更新、部分列の取得が可能になります。
 Pythonのスライスは、[start:stop:step] の形式で指定します。
 start: スライスの開始位置(この位置の要素を含む)
 stop: スライスの終了位置(この位置の要素を含まない)
 step: スライスのステップ

 以下に、スライスの基本的な使い方と例を示します。

(1)リストのスライス

[2, 3, 4, 5]
[0, 2, 4]
[5, 6, 7, 8, 9]
[0, 1, 2, 3, 4]
[0, 2, 4, 6, 8]
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

(2)文字列のスライス

Hello
World!
!dlroW ,olleH

(3)1次元配列のスライス

[2 3 4 5]
[0 2 4]

(4)2次元配列のスライス

[2 3 4 5]
[0 2 4]

(5)3次元配列のスライス

(6)スライスオブジェクト
 スライスオブジェクトはPythonのslice型で、スライスの開始、終了、およびステップを格納します。

[2, 3, 4, 5]
[0, 2, 4]

トランスフォーマー(Transformer)

1.概要
トランスフォーマーの元となるアーキテクチャは、以下の論文で発表されました:
 Title: Attention Is All You Need
 Authors: Ashish Vaswani, Noam Shazeer, Niki Parmar, Jakob Uszkoreit, Llion Jones, Aidan N. Gomez,
 Łukasz Kaiser, and Illia Polosukhin
 Published in: NeurIPS 2017 (Conference on Neural Information Processing Systems)
 ChatGPTなどの自然言語処理、画像生成などモデルとして使用されて、主な特徴を以下に示します。

(1)自己注意機構(Self-Attention Mechanism)
 自己注意機構は、入力シーケンス(文章など)の各要素(トークン)のシーケンス全体の他の要素に対する重要性を評価する方法です。これにより、文中の単語同士の関係を捉えることができるようになり、文章の生成において、次に続く単語の選択するときの重みづけ(文脈によって動的に変化する)ができるようになります。

(2)位置エンコーディング(Positional Encoding)
 トランスフォーマーはリカレント(再帰的)な構造を持たないため、入力シーケンスの順序情報を明示的にモデルに提供する必要があります。位置エンコーディングはこの問題を解決するために使用されます。

(3)エンコーダ・デコーダ構造(Encoder-Decoder Structure)
 トランスフォーマーモデルはエンコーダとデコーダの2つの主要部分から構成されています。エンコーダーは、入力された文章を固定長のベクトル(文脈ベクトル)に変換する役割をもちます。デコーダはエンコーダーが生成した文脈ベクトルを元に推論した結果(出力)を生成する役割をもちます。

(4)並列計算の効率性
 トランスフォーマーは並列計算に適しており、従来のRNN(Recurrent Neural Networks)やLSTM(Long Short-Term Memory)に比べてトレーニング速度が速いという利点があります。これは特に大規模なデータセットでの学習においてメリットとなります。

(5)スケーラビリティ
 トランスフォーマーは非常に大規模なモデルにスケールさせることができ、多くのパラメータを持つことで高い表現力を持っています。例えば、BERTやGPTなどの大規模なトランスフォーマーモデルは数億から数十億のパラメータを持ち、非常に高い性能を発揮します。

(6)多用途性
 トランスフォーマーは機械翻訳、テキスト生成、要約、質問応答、画像認識など、さまざまなタスクに適用可能です。これは、トランスフォーマーがシーケンスの関係性をうまく捉えることができるためです。

2.トランスフォーマーの構成
 以上の特徴をモデルの各部分について考察していきます。
 大まかにトランスフォーマーはエンコーダーとデコーダに分けられます。エンコーダで入力データを埋め込みベクトル空間に変換します。

arXiv:170 2 Aug 2023「Attention Is All You Need」から引用

文章の生成を行う場合を例に、エンコーダーとデコーダの働きについて見ていきます。

2.1 エンコーダーの働き
 Inputsで文章を入力し、入力済みの文章に続く単語(トークン)の予測の基となるベクトルを作成するまでがエンコーダーの機能となります。
 Inputsから順番に見ていきます。

①InputsとinputEmbedding
 文章を構成する全ての単語を事前学習したEmbededdingのデータで数値ベクトルに変換します。これを埋め込みベクトルといいます。ここでは単語から一意的にEmbededdingのデータに置き換えるだけで文脈は考慮されていません。

②PositionalEncoding
 ①のinputの文章の各単語をEmbeddingベクトル空間に置き換えたベクトルデータに対して、文脈上の単語の位置情報を加えます。
 (位置エンコーディングの計算方法)
 位置エンコーディングは、サイン関数とコサイン関数を使用して計算されます。具体的には、次のような形で各次元ごとに計算されます:

この位置エンコーディングにおける各次元は、異なる周期や位相を持つ関数の値を表し、第一次元は高周波で周期的な情報を担い、第二次元は低周波で周期的な情報を担うといった役割をします。

③Multi-HeadAttention

(a)入力ベクトルV、K、Qの準備
  入力シーケンスを受け取り、Query(Q)、Key(K)、Value(V)を生成します

(b)Linear変換
 Linear変換は、Q、K、Vそれぞれに対して線形変換(入力ベクトルに重み行列を掛け合わせ、バイアスベクトルを加える操作)を適用し、新しいQ’、K’、V’を得ます。線形変換は以下の式で表されます

Multi-Head Attentionでは、以下のようにQuery、Key、Valueそれぞれに異なる重み行列を適用します。

(c)ScaledDot-ProductAttention
 ScaledDot-ProductAttentionは、Queryベクトル(入力シーケンスで注意を払う単語)と入力シーケンスの他の要素のKeyベクトル、Valueベクトルから下記の計算により、スコアを求めます。

Scaled Dot-Product Attentionの出力は、入力のValue行列Vと同じ形状を持ちます。
 具体的には、出力の形状はVの形状(バッチサイズ、シーケンス長、埋め込み次元)と一致します。

 Multi-Head Attention機構においてはこれらの処理はヘッドが複数あり、各ヘッドは異なる部分空間(埋め込みベクトルの異なる次元)で独自のScaled Dot-Product Attentionを計算し、その結果を結合して最終的な出力を生成します。例として、埋め込み次元が8でヘッド数が4のときは各ヘッドの次元は8/4=2となります。

(c)Concat
 各ヘッドの出力を結合し、最終的な出力を得ます。

④Add&Norm
(a)Add(残差接続)
 勾配消失問題を軽減し、トレーニングの安定性を高めるため入力(Query)をその層の出力に直接足し合わせます。
 Output=Layer Output+Input(ここではQuery)

(b)Norm(正規化) 
 正規化は各層の出力のスケールを調整しトレーニングの安定性と収束速度を向上させます。

⑤Feedward
 フィードフォワードニューラルネットワーク(Feedforward Neural Network、FFN)は、トランスフォーマーモデルの各エンコーダーおよびデコーダーブロック内で、トークンごとの情報を変換し、特徴抽出や複雑なパターンを学習するためのものです。自己注意機構が各トークン間の相関を捉えるのに対して、FFNは各トークンの特徴を独立に変換し、より高度な特徴を学習する役割を担います。具体的には最初の線形変換で高次元に変換し、非線形活性化関数を適用し、最後に線形変換で元の次元に戻します。

⑥Add&Norm
(a)Add(残差接続)
 勾配消失問題を軽減し、トレーニングの安定性を高めるため入力(Query)をその層の出力に直接足し合わせます。
 Output=Layer Output+Input(ここではQuery)

(b)Norm(正規化) 
 正規化は各層の出力のスケールを調整しトレーニングの安定性と収束速度を向上させます。

2.2 デコーダの働き
 トランスフォーマーモデルのデコーダは、エンコーダから得られたコンテキスト情報をもとに、逐次的に出力シーケンスを生成する役割を担います
①OutputsとOutputsEmbedding
 現在までに生成された部分シーケンスのトークンをベクトル表現に変換したものです。通常、これらのベクトルはEmbedding層を通じて得られます。
 Embedding層は、語彙内の各トークンを固定次元の連続値ベクトルにマッピングします。

②PositionalEncoding
 ①のoutputの文章の各単語をEmbeddingベクトル空間に置き換えたベクトルデータに対して、文脈上の単語の位置情報を加えます。
 位置ベクトルの計算方法は2.1項と同じです。

③Masked Multi-Head Attention
 Masked Multi-Head Attention (Masked MHA) は、トランスフォーマーのデコーダで使用される自己注意機構の一部です。マスク付き自己注意機構は、未来の情報を見ないようにするためにマスクを適用します。

④Multi-Head Attention
 デコーダのMulti-Head Attentionの入力にエンコーダからの出力が入っている理由は、エンコーダが入力シーケンスの各トークンの特徴を含むコンテキスト情報を提供するためです。これにより、デコーダは現在生成しているトークンが入力シーケンスのどの部分と関係しているかを学習できます。

⑤Linear
 デコーダの出力は、通常、トークンの埋め込み次元を持つベクトルです。このベクトルを、単語サイズの次元に変換する必要があります。線形層は、デコーダの出力ベクトルに対して線形変換を適用し、単語サイズに対応するスコアベクトルを生成します。

⑥softmax
 線形層からの出力のスコアにソフトマックス関数を適用することで確率分布に変換します。ソフトマックス関数は、各スコアを指数関数的に変換し、全体のスコアの合計が1になるように正規化します。

3.サンプル
 トランスフォーマーのモデルを簡略化したエンコーダデコーダモデル(下記のブロック図)について実装し、確認を行います。

・前提条件
 単語のエンベディングベクトルは回答が合うように手動で設定した
 embeddings = {
“りんご”: np.array([0.8, 0.0, 0.8, 0.2]),
“赤い”: np.array([0.8, 0.5, 0.8, 0.2]),
“みかん”: np.array([0.1, 0.9, 0.1, 0.8]),
“黄色い”: np.array([0.3, 0.8, 0.3, 0.8]),
“の”: np.array([0.2, 0.3, 0.1, 0.2]),
“色”: np.array([0.3, 0.2, 0.2, 0.3]),
“は”: np.array([0.1, 0.2, 0.2, 0.2])
}

・実行環境
Windows 10 Pro
Python 3.12.4
numpy 1.26.4

・実行結果

similarities: [1.23933206 1.39950039 1.39310835 1.60962455 0.5542311 0.76903867
0.46902724]
次に来る単語は: 黄色い #単語の4番目

(パラメータ数)
(計算例)
ボキャブラリサイズ:7
エンベデイングサイズ:4
マルチヘッドアッテンションのヘッド数:2
エンコーダのマルチヘッドアッテンション層:1
デコーダのマルチヘッドアッテンション層:2
のときのパラメータ数を求める

1.エンベディング層のパラメータ数

2.マルチヘッドアテンション層のパラメータ数

3.フィードフォワードネットワーク層のパラメータ数(サンプルでは省略)

4.エンコーダ層のパラメータ数
 エンコーダ層はマルチヘッドアテンション層1つとフィードフォワード層1つ(仮定)を持ちます。

次に処理の順番に各ベクトルのパラメータの変化を示します。

c言語 関数ポインタ

 C言語では、関数ポインタは関数のアドレスを格納する変数です。これにより、プログラムは関数を間接的に呼び出すことができます。関数ポインターは、コールバック関数の実装、動的ディスパッチの実装、ポリモーフィズムのメカニズムの提供など、さまざまな方法で使用できます。
 この例では、2 つの整数を取り、整数を返す関数”add”を指す関数ポインタを宣言します。次に、関数”add”のアドレスをポインターに割り当てます。関数ポインタに2 つの整数を引数として渡すことにより、ポインターを介して関数”add”を間接的に呼び出します。

サンプル
fpointer_sample1.c

実行結果

end

c言語 配列と線形リスト

1.配列とは
 配列は、同じデータ型の要素を番号(添字:インデックス)順に並べたものを表します。
 添字は、C言語を含む多くの言語は0スタートとなります。
 (例)要素数100のときの1次元配列a、a[0]・・・a[99]

配列のサンプル(1)
list_sample1.c

例では、配列の要素数MAX_SIZE変数を定義します。insert関数はリストの末尾に新しい要素を挿入し、remov関数は特定のインデックスの要素を削除します。get関数は特定のインデックスの要素を返し、search関数は特定の値を持つ要素を検索してそのインデックスを返します。
 ここでは要素はint型としていますが、他のデータ型の要素を格納する必要がある場合は、配列listのデータ型と関数のパラメーターと戻り値を変更する必要があります。

実行結果

配列のサンプル(2)
list_sample2.c

 この例では構造体を作成して、構造体を呼び出して配列を操作します。

実行結果

2.線形リスト
 線形リストは、データと「次のデータを指し示すポインタ(場所)」が入ったノードと呼ばれる要素がポインタでつながっているデータ構造で、隣接するデータ同士をポインタで連結して表現します。

線形リストのサンプル
list_sample3.c

データとリスト内の次のノードへのポインターを保持するために、Nodeと呼ばれる構造体を定義します。関数insertはリストの最後に新しいノードを追加し、関数displayは各ノードのデータを出力します。
 main関数では、3 つの値を挿入し、結果のリストを表示します。

実行結果

end

c言語 スレッド間の排他制御

1.概要
 複数のスレッドで共有リソースにアクセスするとき、同時に1つのスレッドだけが共有リソースにアクセスできるようにするために、ミューテックス (英: mutex)が使用されます。

2.ミューテックスを使った排他制御
 下記のサンプルでは、2 つのスレッドを作成し、実行するスレッド関数としてthread_functionを渡します。thread_function内では、最初に pthread_mutex_lockを使用してミューテックスをロックし、一度に 1 つのスレッドだけが共有リソースにアクセスできるようにします。共有リソースにアクセスした後、pthread_mutex_unlockを使用してミューテックスのロックを解除します。メインでは、pthread_joinを使用して両方のスレッドが終了するのを待ち、最後に pthread_mutex_destroy を使用してミューテックスを破棄します。
 ミューテックスを正しく使用しないと、デッドロックが発生する可能性があります。デッドロックは、2 つ以上のスレッドがブロックされ、互いがミューテックスを解放するのを待っているときに発生します。デッドロックを回避するには、複数のミューテックスにアクセスするときは常に同じロック順序を使用します。

3.サンプル
thread_mutex1.c

(※1)pthread_create()
新しいスレッドを生成する
(書式)
#include <pthread.h>
int pthread_create(pthread_t * thread,
pthread_attr_t * attr,
void * (*start_routine)(void *),void * arg);
引数:
thread:スレッド管理用
attr:スレッド属性、NULLのときはデフォルト
(*start_routine)(void *):スレッドから呼び出される関数へのポインタ
arg:start_routine()の引数で渡すデータのポインタ
戻り値:
成功すると新しく作成したスレッドの識別子が引数threadの指す領域へ格納され、0 が返る。エラーの場合、非 0 のエラーコードが返る。

(※2)pthread_join()
別のスレッドの終了を待つ
(書式)
#include <pthread.h>
int pthread_join(pthread_t th, void **thread_return);
引数:
th:待機するスレッドの指定
**thread_return:スレッドの戻り値を格納する領域
戻り値:
成功すると、thの返り値がthread_returnで指し示された領域に格納され、0が返る。エラーの場合、非 0 のエラーコードが返る。

(※3)pthread_self()
呼び出したスレッドのIDを取得する
(書式)
#include <pthread.h>
pthread_t pthread_self(void);


コンパイルするとき、マルチスレッドをサポートするために、pthreads ライブラリーを使用するようにコンパイラーに指示します。
$ gcc -pthread thread_mutex1.c

実行結果

end

c言語 コールバック関数

1.概要
 コールバック関数 (関数ポインターとも呼ばれます) は、別の関数(ハンドラ)に引数として渡される関数です。

2.コールバック関数を使用する利点
(1)柔軟性
 別の関数をコールバックとして渡すことにより、実行時に関数の動作をカスタマイズできるため、関数の使用における柔軟性が向上します。これにより、コードをよりモジュール化して再利用可能にすることができます。
(2)非同期プログラミング
 コールバックを使用して非同期処理をすることができます。

3.C言語コールバック関数の例

callback_sample1.c

実行例

end