生産がす
なんか げーむ つくったり とか してます
2025年11月13日木曜日
FolderPathLogger ver1.01
■ダウンロード
FolderPathLogger101.zip
size: 113KB
md5: 3d134fcca1d69d0a71edfbb137c2f3a8
分類: フリーソフト
■概要
表示したフォルダのパスを収集します。
間違って閉じてしまったときや、深い位置にあるフォルダをたどるのが面倒な時などにどうぞ。
■動作環境
Windows 11
CPU 2GHz以上
メモリ 2GB以上
■アンインストール
レジストリは触っておりません。
削除の際はフォルダごとゴミ箱へ。
■使い方
起動中はエクスプローラで表示したフォルダのパスを自動で収集します、
●リスト右クリックメニュー
Open Dir - 指定フォルダを開きます
Copy as Text - 指定フォルダのパスをテキスト形式でコピ。複数選択時は改行してコピーします
Select All - 項目を全選択
Deselect - 選択を解除
Remove - 選択している項目を削除
Remove All - 全項目を削除
Remove Invalid Paths - 存在しないフォルダのパスであれば、項目を削除
Search - 検索欄に入力された文字に合致するパスを表示するようにします
●ウィンドウメニュー
save - リストとコンフィグを直ちに保存します
exit - プログラムを終了します
config - コンフィグウィンドウを表示
Version - バージョン情報を表示
●コンフィグウィンドウ
最大保持数 - リストに保持できる項目の最大数
オートセーブ - リストとコンフィグの自動保存周期の設定
項目の背景色 - パスのドライブ別の背景色を設定できます。
色は「色の設定ウィンドウ」の「作成した色」のものから使用されます
重複パスをスキップ
前回と同じパスを確認したときにリストに追加しないようにします
■そのほか
このソフトウェアを使用し、何らかの障害が発生しても責任を取りかねますのでご了承ください。
minizを使用しています
Copyright 2013-2014 RAD Game Tools and Valve Software
Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC
■更新履歴
2025-11-12 ver1.01
64bit化
アクティブウィンドウの変更感知にSetWinEventHookを使用するように変更。
エクスプローラのパス変更をIConnectionPointContainerを使用するよう変更。
試しにリストデータの圧縮保存にminizを使用するよう変更
リストデータのメモリをメモリプールから取得するよう変更
前バージョンとの違い
コンパクトパスを削除
リスト項目の背景色をドライブ別のみに
フォルダコピー機能を削除
2020-05-25 ver1.00c
エクスプローラからパスを得るためのデータを最小限だけ保持するよう変更
2020-05-24
エクスプローラからパスを得るためのデータを保持しない仕様に変更。
2019-11-18 ver1.00b
ウィンドウ生成周りが間違っていたのを修正
2019-03-26 ver1.00a
windowsに処理負担がかかりHookが死ぬ場合に備えて、Hookしなおす処理を追加
2018-10-22 ver1.00
公開
2025年10月26日日曜日
windowCap ver1.04
■ダウンロード
windowCap104.zip
size: 170KB
分類: フリーソフト
MD5: 7b113a7d46ddede220ef03b8db483cba
■概要
PrintScreenが押されると、ウィンドウのスクリーンショットを撮って画像ファイルで出力します。
対応出力形式:BMP、PNG
ShiftやCtrl、Altキーの同時押しによる、キャプチャ範囲の個別設定が可能。(3つまで)
音とバルーン表示でわかりやすく出力確認。
出力ファイル名に日付を入れるといった、ちょっとだけ柔軟な名前設定もできます。
■動作環境
Windows 11
CPU 1GHz以上
メモリ 1GB以上
■アンインストール
レジストリは触っておりません。
削除の際はフォルダごとゴミ箱へ。
■使い方
1. キー設定
キャプチャ方法と保存アクションを設定します。最大3つのキー設定を登録できます。
スクリーンショットのキー設定
キャプチャを実行するキー(PrintScreen固定)と同時に押す修飾キー(Shift、Ctrl、Alt)を設定します。
複数設定の同時実行
3つの設定で同じ記録キーを使用した場合、設定された範囲すべてがキャプチャされ、複数枚の画像ファイルが出力されます。
クリップボード出力時の優先度
クリップボードへ出力する場合、設定リストの下側に登録された設定が優先されます。
記録する画面の範囲(キャプチャ範囲)
・なし
キーが押されても何もしません。(設定を無効化したい場合に)
・メインディスプレイ
メイン画面全体をキャプチャします。
・アクティブウィンドウ
現在操作しているウィンドウ全体(枠、タイトルバー含む)をキャプチャします。
・アクティブウィンドウのクライアント領域
現在操作しているウィンドウから、タイトルバーなどの枠を除いた中身の領域のみをキャプチャします。
・カーソル下のウィンドウ
マウスカーソルの直下にあるウィンドウ全体をキャプチャします。
・カーソル下のウィンドウのクライアント領域
マウスカーソルの直下にあるウィンドウの中身の領域のみをキャプチャします。
・各ディスプレイを個別ファイル
マルチディスプレイ環境で、各画面を別々の画像ファイルとしてキャプチャします。
ファイル名には自動で (1), (2) などが追記されます。
※ クリップボード出力ではメインディスプレイのみが対象になります。
・カーソル下のメインディスプレイ
マウスカーソルがあるディスプレイ全体をキャプチャします。
・全ディスプレイを結合
ディスプレイ全部を範囲とします
マウスカーソルを含める
チェックをいれると、キャプチャ画像にマウスカーソルの形を含めます。
出力先
キャプチャした画像の保存方法を「ファイル」または「クリップボード」から選択します。
2. 通知設定
ポップアップ通知
キャプチャ成功時、画面端にポップアップ通知を表示してお知らせします。
完了音を鳴らす
キャプチャ成功時、音を鳴らしてお知らせします。
※ 実行ファイルと同フォルダにある sound.wav を置き換えることで、好きな音に変更できます。
3. ファイル設定
ファイル形式
保存する画像ファイルの形式を ビットマップ (.bmp) または PNG (.png) から選択します。
上書き時に確認ウィンドウを出す
ファイル名が既存のファイルと重複した場合に、上書きするかどうかを確認するダイアログを表示します。
※ チェックなしの場合、自動で上書きされます。
出力先フォルダ
画像ファイルが保存されるフォルダを指定します。指定フォルダが存在しないとファイルは出力されません。
[...]ボタン
フォルダ選択ウィンドウを開き、出力先フォルダを設定できます。
※ フォルダが存在しない場合、画像が保存されないので注意してください。
出力先フォルダを開く
現在設定されている保存先フォルダをエクスプローラーで開きます。
ファイル名
画像ファイル名を自由に設定できます。
禁止文字
ファイル名には「\」「/」?」「*」「"」「>」「<」「|」の文字は設定できません。
自動置換コード
以下のコードを使用すると、自動的に日付・時刻・連番の数値に置き換えられます。
「%y」年4桁 例: 2015
「%Y」年(下2桁) 例: 15
「%m」月 例: 2
「%M」月(下2桁) 例: 02
「%d」日 例: 3
「%D」日(2桁) 例: 03
「%h」時 例: 1
「%H」時(2桁) 例: 01
「%i」分 例: 1
「%I」分(2桁) 例: 01
「%s」秒) 例: 1
「%S」秒(2桁)) 例: 01
「%l」ミリ秒) 例: 23
「%L」ミリ秒(3桁)例: 023
「%n」連番値 例: 12
「%%」文字の% 例: %
ファイル名例
[%y%m%d]cap%n → [2015213]cap0001
[%Y%M%d]cap%n → [150213]cap0001
[%Y-%M-%d]cap%n → [15-02-13]cap0001
[%y%M%d_%H%I%S]cap%n → [20150213_010101]cap0001
拡張子はファイル形式で設定した形式(例:.png)が自動的に付与されます。
連番値
画像が出力されるごとに+1されていく数値です。ファイル名設定の %n で使用されます。
連番桁数
ファイル名に %n を使用したとき、連番の桁数を設定します。
例: 4桁設定時、連番値が 1 ならファイル名には 0001 と出力されます。
4. 画像メモリをキャッシュする
キャプチャにつかったメモリを破棄せずにとっておくことで高速化を図ります
5. キーチェックを正確に行う
グローバルフックを使いキー入力のタイムラグがほぼ0になるよう努めます
■そのほか
このソフトウェアを使用し、何らかの障害が発生しても責任を取りかねますのでご了承ください。
zlibライブラリを使用しています。
zlib software copyright ? 1995-2022 Jean-loup Gailly and Mark Adler.
https://zlib.net/
libpngライブラリを使用しています。
* Copyright (c) 1995-2025 The PNG Reference Library Authors.
* Copyright (c) 2018-2025 Cosmin Truta.
* Copyright (c) 2000-2002, 2004, 2006-2018 Glenn Randers-Pehrson.
* Copyright (c) 1996-1997 Andreas Dilger.
* Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
https://www.libpng.org/
■更新履歴
2025-10-26 ver 1.04
64bit化
バルーンをクリック時に保存先フォルダを表示可能に
「全ディスプレイを結合」設定を追加
いくつかの設定名をわかりやすいよう変更
(1.01,1.02,1.03台のソースコードがどっか行ったので書き直した)
2015-02-15 ver 1.00a
終了確認メッセージウィンドウが出ているときにキャプチャすると強制終了していたのを修正
2015-02-14 ver 1.00
2025年8月27日水曜日
簡易そろばん (12桁)
簡易そろばん (12桁)
現在の値: 0
*要javascript
意外にそろばんを操作できるだけのウェブページが見当たらなかったの自作しました。
そろばん5級相当の問題をランダムで作成
足し算問題の途中経過が欲しくて問題作成機能を作ってみました。
2025年2月6日木曜日
Fスクリーンキーボード ver3.1c
■ダウンロード
File: fkey310c.zip
Size: 724KB
MD5: 67a1465080e5864acfc93f0693280253
分類: フリーソフト
■概要
スクリーンキーボードにフリック機能をつけて、ctrlやshiftへのカーソル移動の手間を少し減らすことができます。
従来の日本語キーボードの他に、日本語フリック入力用のレイアウトや、テンキーや50音キーボードなども使用できます。
■動作環境
Windows 10 (64bit)
CPU 2GHz以上
メモリ 4GB以上
■アンインストール
レジストリは触っていません。
削除の際はフォルダごとゴミ箱へ。
■使い方
・表示されているキーを左クリックすることでキー入力できます。
・フリック(クリックしたままカーソルを移動し、指を離す)することで
ShiftやCtrlキーを付加したり、関連したキーを押すことができます。
・右クリックフリックで最小化やコンフィグ表示、レイアウト変更などを行うことができます。
右クリックフリックは設定で自由に変更できます
詳しくはhelp.htmlを参照してください。
■そのほか
このソフトウェアを使用し、何らかの障害が発生しても責任を取りかねますのでご了承ください。
■更新履歴
2025-02-05 ver 3.1c
アクティブ時間が500ms以上のウィンドウがアクティブ化対象になるよう修正
2022-09-29 ver 3.1b
スタンバイ時、再起動時に設定を保存するよう修正
2022-07-14 ver 3.1a
テンキーのEnterキーにextendedフラグを付与してみる
2022-04-29 ver 3.1
64ビット化
「前のディスプレイへ移動」「次のディスプレイへ移動」のシステムフリック項目を追加
タイトルバー非表示のときに閉じるボタン等が動作しないように修正
コンフィグウィンドウの表記と動作を少し修正
「ローマ字入力」「スキャンコードを使用」の二つを有効にしているときに、ひらがなの入力がうまく入力できないのを修正
2022-02-16 ver 3.01
デフォルトサイズの1.25倍~4倍のサイズに変更するシステムフリック項目を追加
2022-01-08 ver 3.00b
最小化した状態で終了すると、次に起動時にウィンドウがどっか行く問題を修正。
最小化状態でシステムメニューを出したとき、別ウィンドウアクティブ化する機能が働いてシステムメニューが閉じられてしまう問題を修正。
2021-06-02 ver 3.00a
テンキーexの追加。
スキャンコード使用時のコードが間違ってるのを修正-
2021-02-07 ver 3.00
2023年1月10日火曜日
pointClick ver 2.00d
■ダウンロード
File: pointClick200d.zip
Size: 68KB
分類: フリーソフト
MD5: 8a7d21f79e995d01df32ae8fffa8233c
■概要
クリックしたときに、マウスがちょっと動いてしまってドラッグ&ドロップになってしまうのを防ぎます。
■動作環境
Windows 10 (64bit)
CPU 2GHz以上
メモリ 2GB以上
■アンインストール
レジストリは触っておりません。
削除の際はフォルダごとゴミ箱へ。
■使い方
起動中、クリック開始位置と終了位置が近ければカーソル位置を補正するようになります。
不要になったら終了してください。
■ver1台との違い
クリックに情報を乗せられることに気が付いたので、それを利用してプログラムをシンプルにしました。
マウスのボタンを押してからクリックさせるまでの間に、クリック先ウィンドウが補正機能対象かチェックするようにしました
権限が上と思われるソフトに対して、全機能がオフになる処理を入れました
■設定の説明
○マスター on/off
オフにすると補正機能が動かなくなります。一時的に補正したくないときなどに。
○左クリック、ホイールクリック、右クリック
チェックがついているものだけ補正機能が働きます。
○有効時間
マウスのボタンを押してから離すまでの時間設定
設定した時間よりも長くマウスのボタンを押されていると、位置補正機能は働きません
○有効距離
マウスのボタンを押してから離すまでに移動した距離の設定
設定した距離よりも長く移動していたら、位置補正機能は働きません
○クリック時間
補正機能が実行されたときにクリックされている時間の設定
いくつかのアプリケーションではクリック時間が短すぎるとクリックされたと認識しないことがあります。
○有効アプリを限定する
補正機能が働くアプリケーションを限定することができます
○アプリ選択
補正機能が働くアプリケーションを選択できます
○ログ表示
補正機能が働いたかどうかのログを表示します
○ログ表示数
ログを表示する数の設定
■そのほか
このソフトウェアを使用し、何らかの障害が発生しても責任を取りかねますのでご了承ください。
■更新履歴
2023-01-07 ver 2.00d
新規ウィンドウをクリックしたときのフックし直す処理を最適化。
2021-03-08 ver 2.00c
D&D開始時の位置がずれる問題を軽減できるよう修正。他のアプリケーションの処理サイクルやPC全体の負担具合によって機能したりしなかったりします。
2020-12-17 ver 2.00b
最小化時に終了して、使い起動時に最小化状態が維持していないのを修正
2019-11-10 ver 2.00a
権限が上と思われるソフトがアクティブのときは、全機能オフにするよう修正
2022年12月20日火曜日
C++ neural networkの勉強
■行列を使うタイプのニューラルネットワークを作ってみました。
参考にしたのは
10.1: Introduction to Neural Networks - The Nature of Code https://www.youtube.com/watch?v=XJ7HLz9VYz0
C++ neural networks from scratch - Pt 1. building a matrix library https://www.lyndonduong.com/linalg-cpp/
まずは作ってみようと思って作ったのがこちら。
[ソースコード]
ちょっと前に作ったjavascript版のニューラルネットワークと同様に、
HTMLカラーコードを学習させることにしました。
xor以外で手ごろな大きさの学習が意外にぱっと出てこないから仕方ないね。
ときどき学習させるとエラーが肥大化していって、最後はdoubleがNaNになって死ぬことがあって、
これが最後まで改善できませんでした:<。
今回はカラーコードのIndexを出力できればいいので、
oneHotを導入することで学習がアバウトでも動作するようにしてみました。
oneHotなしだと途中で学習が停滞してしまうことが多くて、たぶんニューロンが少ないんだろうなと思ってるけど詳細は不明。
そのままでは処理が遅すぎてどうしようもないと思ったので、適当に最適化してみたのがこちら。
[ソースコード]
最適化の方針としては、
・Matrixを二次元配列から一次元配列にしてキャッシュに乗りやすく。これは参考サイトさんほぼそのままです。
・行列の乗算時のアドレス計算を、ポインタを使用して掛け算をできるだけ消しました。乗除算は基本的に重いので繰り返し処理の内側ではできるだけ減らしたい。
・行列の乗算を、計算順序を変えてキャッシュに乗りやすいらしい順序に変更。
・transpose処理いらなくない?
と思って専用の乗算処理を用意してみました。乗算処理内でアドレス計算を縦横入れ替えています。
・行列用のメモリを確保するの重いので、メモリが足りていればメモリ確保しないようにして一時変数として使いまわすようにしています。
最終的には既存のライブラリを使うほうが早いし汎用性もあるということになるので、深入りはやめました。
GPU使うなり、マルチスレッドを使うなりあると思います。
参考にしたのは
10.1: Introduction to Neural Networks - The Nature of Code https://www.youtube.com/watch?v=XJ7HLz9VYz0
C++ neural networks from scratch - Pt 1. building a matrix library https://www.lyndonduong.com/linalg-cpp/
まずは作ってみようと思って作ったのがこちら。
[ソースコード]
#include <cstdio>
#include <cmath>
#include <vector>
#include <random>
#include <future>
#include <iostream>
#include <fstream>
#include <string>
class Matrix
{
public:
static Matrix matmul( const Matrix& a, const Matrix& b )
{
Matrix ret( a.m_rows, b.m_cols );
if ( a.m_cols == b.m_rows )
{
for ( int y = 0; y < ret.m_rows; y++ )
{
for ( int x = 0; x < ret.m_cols; x++ )
{
double sum = 0;
for ( int z = 0; z < a.m_cols; ++z )
{
sum += a.m_values[y][z] * b.m_values[z][x];
}
ret.m_values[y][x] = sum;
}
}
}
else
{
std::printf("%s - miss match matrix.[%dx%d]x[%dx%d]\n", __FUNCSIG__, a.m_rows, a.m_cols, b.m_rows, b.m_cols );
}
return ret;
}
static Matrix sub( const Matrix& a, const Matrix& b )
{
Matrix ret( a.m_rows, a.m_cols );
if ( a.m_cols == b.m_cols &&
a.m_rows == b.m_rows )
{
for ( int y = 0; y < ret.m_rows; y++ )
{
for ( int x = 0; x < ret.m_cols; x++ )
{
ret.m_values[y][x] = a.m_values[y][x] - b.m_values[y][x];
}
}
}
return ret;
}
static Matrix createFromArray( const std::vector<double>& array )
{
Matrix ret( array.size(), 1 );
for ( int i = 0; i<array.size(); ++i )
{
ret.m_values[i][0] = array[i];
}
return ret;
}
static Matrix transpose( const Matrix& m )
{
Matrix ret( m.m_cols, m.m_rows );
for ( int y = 0; y < ret.m_rows; ++y )
{
for ( int x = 0; x < ret.m_cols; ++x )
{
ret.m_values[y][x] = m.m_values[x][y];
}
}
return ret;
}
public:
Matrix( int rows, int cols )
: m_rows( rows )
, m_cols( cols )
{
m_values.resize( rows );
for ( auto& vec : m_values )
{
vec.resize( cols );
}
}
Matrix( const Matrix& m )
: m_rows( m.m_rows )
, m_cols( m.m_cols )
, m_values( m.m_values )
{
}
Matrix& operator = ( const Matrix& m )
{
m_values = m.m_values;
m_rows = m.m_rows;
m_cols = m.m_cols;
return *this;
}
double& getValue( int x, int y )
{
return m_values[y][x];
}
const double& getValue( int x, int y ) const
{
return m_values[y][x];
}
void randomizeN()
{
std::random_device rndDev;
std::mt19937 random( rndDev() );
std::uniform_real_distribution<double> dist( 0, 1.0 / std::sqrt( static_cast<double>(size()) ) );
for ( auto& v : m_values )
{
for ( auto& d : v )
{
d = dist( random );
}
}
}
void multiplyElementwise( const Matrix& m )
{
for ( int y = 0; y < m_rows; ++y )
{
for ( int x = 0; x < m_cols; ++x )
{
m_values[y][x] *= m.m_values[y][x];
}
}
}
void multiplyScalar( double n )
{
for ( auto& r : m_values )
{
for ( auto& c : r )
{
c *= n;
}
}
}
void add( double n )
{
for ( auto& r : m_values )
{
for ( auto& c : r )
{
c += n;
}
}
}
void add( const Matrix& m )
{
if ( m_rows != m.m_rows || m_cols != m.m_cols ) return;
for ( int y = 0; y < m_rows; ++y )
{
for ( int x = 0; x < m_cols; ++x )
{
m_values[y][x] += m.m_values[y][x];
}
}
}
Matrix& operator + ( const Matrix& m )
{
add( m );
return *this;
}
void map( std::function<double(const double&)> fn )
{
for ( int y = 0; y < m_rows; ++y )
{
for ( int x = 0; x < m_cols; ++x )
{
m_values[y][x] = fn( m_values[y][x] );
}
}
}
void print()
{
std::printf("%s - rows: %d, cols: %d\n", __FUNCSIG__, m_rows, m_cols );
for ( int y = 0; y < m_values.size(); ++y )
{
for ( int x = 0; x < m_values[y].size(); ++x )
{
std::printf("%f, ", m_values[y][x]);
}
std::printf( "\n" );
}
}
int size() const
{
return m_rows * m_cols;
}
int m_rows;
int m_cols;
std::vector<std::vector<double>> m_values;
};
class NeuralNetwork
{
public:
static double sigmoid( const double& d )
{
return 1.0 / (1.0 + std::exp( -d ));
}
static double leaklyRelu( const double& d )
{
return std::max( d * 0.01, d );
//return std::min( std::max( d * 0.01, d ), 6.0 );
}
static double dsigmoid( const double& d )
{
//const double s = sigmoid( d );
//return s * (1 - s);
return d * (1.0 - d);
}
static double dleaklyRelu( const double& d )
{
return (d >= 0.0) ? 1.0 : 0.01;
}
static double tanh( const double& d )
{
return std::tanh( d );
}
static double dtanh( const double& d )
{
return 1.0 - std::pow( std::tanh( d ), 2.0 );
}
public:
NeuralNetwork( int input, int hidden, int output )
{
create( input, hidden, output );
}
void create( int input, int hidden, int output )
{
m_weights.push_back( Matrix( hidden, input ) );
m_weights.push_back( Matrix( output, hidden ) );
for ( auto& m : m_weights )
{
m.randomizeN();
}
m_bias.push_back( Matrix( hidden, 1 ) );
m_bias.push_back( Matrix( output, 1 ) );
for ( auto& m : m_bias )
{
m.randomizeN();
}
m_output.resize( m_bias.size(), Matrix(output, 1) );
}
Matrix foward_prop( const Matrix& input )
{
Matrix m = input;
for ( int i = 0; i < m_weights.size(); ++i )
{
m = Matrix::matmul( m_weights[i], m ) + m_bias[i];
m.map( leaklyRelu );
m_output[i] = m;
}
// 最後の奴だけoneHotしてみる
oneHot( m );
m_output.back() = m;
return m;
}
void step( Matrix& m )
{
for ( int y = 0; y < m.m_rows; ++y )
{
for ( int x = 0; x < m.m_cols; ++x )
{
m.getValue( x, y ) = m.getValue( x, y ) > 0.5 ? 1 : 0;
}
}
}
void oneHot( Matrix& m )
{
int mostTopX = -1;
int mostTopY = -1;
double mostTopValue = -100;
for ( int y = 0; y < m.m_rows; ++y )
{
for ( int x = 0; x < m.m_cols; ++x )
{
double& d = m.getValue( x, y );
if ( d > mostTopValue )
{
mostTopValue = d;
mostTopX = x;
mostTopY = y;
}
d = 0;
}
}
if ( mostTopX >= 0 && mostTopX < m.m_cols &&
mostTopY >= 0 && mostTopY < m.m_rows )
{
m.getValue( mostTopX, mostTopY ) = 1;
}
else
{
std::printf("%s - err. couldnt find most top idx\n", __FUNCSIG__);
}
}
// [参考]part4
void train( const std::vector<std::pair<Matrix, Matrix>>& trainingDataList, double l_rate, int epoch )
{
// 学習をランダムにしてみる
auto trainingDataListShuffle = trainingDataList;
std::random_device randDev;
std::mt19937 randMt( randDev() );
for ( int e = 0; e < epoch; ++e )
{
// 学習順序をランダムに
std::shuffle( trainingDataListShuffle.begin(), trainingDataListShuffle.end(), randMt );
double sumError = 0;
for ( auto& trainingData : trainingDataListShuffle )
{
// forward
Matrix m = foward_prop( trainingData.first );
// error
{
Matrix em = Matrix::sub( trainingData.second, m );
em.map( []( const double& d ) -> double { return d * d; } );
for ( int k = 0; k < em.m_rows; ++k )
{
sumError += em.getValue( 0, k );
}
}
// training
{
Matrix err = Matrix::sub( trainingData.second, m_output.back() );
for ( int i=m_weights.size()-1; i>=0; i--)
{
const Matrix tsWeight = Matrix::transpose( m_weights[i] );
Matrix prevError = Matrix::matmul( tsWeight, err ); //< 前のレイヤー(次の処理)へ渡すerr
Matrix dOutput = m_output[i];
dOutput.map( dleaklyRelu );
Matrix gradients = err;
gradients.multiplyElementwise( dOutput );
gradients.multiplyScalar( l_rate );
const Matrix tsOutput = Matrix::transpose( i>0 ? m_output[i-1] : trainingData.first );
const Matrix weightGrad = Matrix::matmul( gradients, tsOutput );
m_bias[i].add( gradients );
m_weights[i].add( weightGrad );
err = prevError;
}
}
}
std::printf("[%d] %f\n", e, sumError);
}
}
void print()
{
std::printf( "%s\n", __FUNCSIG__ );
std::printf( "weights\n" );
for ( int i = 0; i < m_weights.size(); ++i )
{
const Matrix& m = m_weights[i];
for ( int y = 0; y < m.m_rows; ++y )
{
for ( int x = 0; x < m.m_cols; ++x )
{
std::printf( "[layer:%d][%d][%d]%f\n", i, y, x, m.getValue(x,y) );
}
}
}
std::printf( "bias\n" );
for ( int i = 0; i < m_bias.size(); ++i )
{
const Matrix& m = m_bias[i];
for ( int y = 0; y < m.m_rows; ++y )
{
for ( int x = 0; x < m.m_cols; ++x )
{
std::printf( "[layer:%d][%d][%d]%f\n", i, y, x, m.getValue( x, y ) );
}
}
}
}
bool saveWeights( char* fileName ) const
{
std::ofstream outputFile( fileName );
if ( outputFile.is_open() )
{
// save weights
for ( auto& wm : m_weights )
{
for ( int y = 0; y < wm.m_rows; ++y )
{
for ( int x = 0; x < wm.m_cols; ++x )
{
auto weightStr = std::to_string( wm.getValue( x, y ) );
outputFile << weightStr << ",";
}
}
}
// save bias
for ( auto& bm : m_bias )
{
for ( int y = 0; y < bm.m_rows; ++y )
{
for ( int x = 0; x < bm.m_cols; ++x )
{
auto weightStr = std::to_string( bm.getValue( x, y ) );
outputFile << weightStr << ",";
}
}
}
outputFile.close();
return true;
}
return false;
}
bool loadWeights( char* fileName )
{
if ( fileName )
{
std::vector<double> weightsList;
// read weights file
{
std::ifstream inputFile( fileName );
if ( inputFile.is_open() )
{
std::string str;
while ( std::getline( inputFile, str, ',' ) )
{
double d = std::stod( str );
weightsList.push_back( d );
}
}
inputFile.close();
}
if ( weightsList.empty() ) return false;
// update weights
int idx = 0;
for ( auto& wm : m_weights )
{
for ( int y = 0; y < wm.m_rows; ++y )
{
for ( int x = 0; x < wm.m_cols; ++x )
{
if ( weightsList.size() <= idx ) return false;
wm.getValue( x, y ) = weightsList[idx];
idx++;
}
}
}
// update bias
for ( auto& bm : m_bias )
{
for ( int y = 0; y < bm.m_rows; ++y )
{
for ( int x = 0; x < bm.m_cols; ++x )
{
if ( weightsList.size() <= idx ) return false;
bm.getValue( x, y ) = weightsList[idx];
idx++;
}
}
}
return true;
}
return false;
}
//private:
public:
std::vector<Matrix> m_weights;
std::vector<Matrix> m_bias;
std::vector<Matrix> m_output;
};
// [参考]http://www.htmq.com/color/colorname.shtml
// 138 color
struct OriginalColorData
{
double r, g, b;
char* name;
} g_originalColorData[] = {
{ 255, 255, 255, "white" },
{ 245,245,245, "whitesmoke" },
{ 248,248,255, "ghostwhite" },
{ 240,248,255, "aliceblue" },
{ 230,230,250, "lavender" },
{ 240,255,255, "azure" },
{ 224,255,255, "lightcyan" },
{ 245,255,250, "mintcream" },
{ 240,255,240, "honeydew" },
{ 255,255,240, "ivory" },
{ 245,245,220, "beige" },
{ 255,255,224, "lightyellow" },
{ 250,250,210, "lightgoldenrodyellow" },
{ 255,250,205, "lemonchiffon" },
{ 255,250,240, "floralwhite" },
{ 253,245,230, "oldlace" },
{ 255,248,220, "cornsilk" },
{ 255,239,213, "papayawhite" },
{ 255,235,205, "blanchedalmond" },
{ 255,228,196, "bisque" },
{ 255,250,250, "snow" },
{ 250,240,230, "linen" },
{ 250,235,215, "antiquewhite" },
{ 255,245,238, "seashell" },
{ 255,240,245, "lavenderblush" },
{ 255,228,225, "mistyrose" },
{ 220,220,220, "gainsboro" },
{ 211,211,211, "lightgray" },
{ 176,196,222, "lightsteelblue" },
{ 173,216,230, "lightblue" },
{ 135,206,250, "lightskyblue" },
{ 176,224,230, "powderblue" },
{ 175,238,238, "paleturquoise" },
{ 135,206,235, "skyblue" },
{ 102,205,170, "mediumaquamarine" },
{ 127,255,212, "aquamarine" },
{ 152,251,152, "palegreen" },
{ 144,238,144, "lightgreen" },
{ 240,230,140, "khaki" },
{ 238,232,170, "palegoldenrod" },
{ 255,228,181, "moccasin" },
{ 255,222,173, "navajowhite" },
{ 255,218,185, "peachpuff" },
{ 245,222,179, "wheat" },
{ 255,192,203, "pink" },
{ 255,182,193, "lightpink" },
{ 216,191,216, "thistle" },
{ 221,160,221, "plum" },
{ 192,192,192, "silver" },
{ 169,169,169, "darkgray" },
{ 119,136,153, "lightslategray" },
{ 112,128,144, "slategray" },
{ 106,90,205, "slateblue" },
{ 70,130,180, "steelblue" },
{ 123,104,238, "mediumslateblue" },
{ 65,105,225, "royalblue" },
{ 0,0,255, "blue" },
{ 30,144,255, "dodgerblue" },
{ 100,149,237, "cornflowerblue" },
{ 0,191,255, "deepskyblue" },
{ 0,255,255, "cyan(aqua)" },
{ 64,224,208, "turquoise" },
{ 72,209,204, "mediumturquoise" },
{ 0,206,209, "darkturquoise" },
{ 32,178,170, "lightseagreen" },
{ 0,250,154, "mediumspringgreen" },
{ 0,255,127, "springgreen" },
{ 0,255,0, "lime" },
{ 50,205,50, "limegreen" },
{ 154,205,50, "yellowgreen" },
{ 124,252,0, "lawngreen" },
{ 127,255,0, "chartreuse" },
{ 173,255,47, "greenyellow" },
{ 255,255,0, "yellow" },
{ 255,215,0, "gold" },
{ 255,165,0, "orange" },
{ 255,140,0, "darkorange" },
{ 218,165,32, "goldenrod" },
{ 222,184,135, "burlywood" },
{ 210,180,140, "tan" },
{ 244,164,96, "sandybrown" },
{ 233,150,122, "darksalmon" },
{ 240,128,128, "lightcoral" },
{ 250,128,114, "salmon" },
{ 255,160,122, "lightsalmon" },
{ 255,127,80, "coral" },
{ 255,99,71, "tomato" },
{ 255,69,0, "orangered" },
{ 255,0,0, "red" },
{ 255,20,147, "deeppink" },
{ 255,105,180, "hotpink" },
{ 219,112,147, "palevioletred" },
{ 238,130,238, "violet" },
{ 218,112,214, "orchid" },
{ 255,0,255, "magenta(fuchsia)" },
{ 186,85,211, "mediumorchid" },
{ 153,50,204, "darkorchid" },
{ 148,0,211, "darkviolet" },
{ 138,43,226, "blueviolet" },
{ 147,112,219, "mediumpurple" },
{ 128,128,128, "gray" },
{ 0,0,205, "mediumblue" },
{ 0,139,139, "darkcyan" },
{ 95,158,160, "cadetblue" },
{ 143,188,143, "darkseagreen" },
{ 60,179,113, "mediumseagreen" },
{ 0,128,128, "teal" },
{ 34,139,34, "forestgreen" },
{ 46,139,87, "seagreen" },
{ 189,183,107, "darkkhaki" },
{ 205,133,63, "peru" },
{ 220,20,60, "crimson" },
{ 205,92,92, "indianred" },
{ 188,143,143, "rosybrown" },
{ 199,21,133, "mediumvioletred" },
{ 105,105,105, "dimgray" },
{ 0,0,0, "black" },
{ 25,25,112, "midnightblue" },
{ 72,61,139, "darkslateblue" },
{ 0,0,139, "darkblue" },
{ 0,0,128, "navy" },
{ 47,79,79, "darkslategray" },
{ 0,128,0, "green" },
{ 0,100,0, "darkgreen" },
{ 85,107,47, "darkolivegreen" },
{ 107,142,35, "olivedrab" },
{ 128,128,0, "olive" },
{ 184,134,11, "darkgoldenrod" },
{ 210,105,30, "chocolate" },
{ 160,82,45, "sienna" },
{ 139,69,19, "saddlebrown" },
{ 178,34,34, "firebrick" },
{ 165,42,42, "brown" },
{ 128,0,0, "maroon" },
{ 139,0,0, "darkred" },
{ 139,0,139, "darkmagenta" },
{ 128,0,128, "purple" },
{ 75,0,130, "indigo" },
};
static void normalizeColors()
{
for ( auto& data : g_originalColorData )
{
data.r /= 255;
data.g /= 255;
data.b /= 255;
}
}
static std::vector<std::pair<Matrix, Matrix>> createTrainingDataList()
{
std::vector< std::pair<Matrix, Matrix> > ret;
for ( int i = 0; i < _countof( g_originalColorData ); ++i )
{
// input
std::pair<Matrix, Matrix> data( Matrix( 1, 1 ), Matrix( 1, 1 ) );
data.first = Matrix::createFromArray( std::vector<double>( { g_originalColorData[i].r, g_originalColorData[i].g, g_originalColorData[i].b } ) );
// output
std::vector<double> tmp( _countof( g_originalColorData ) );
tmp[i] = 1;
data.second = Matrix::createFromArray( tmp );
ret.push_back( data );
}
return ret;
}
static int getMostTopIdx( const Matrix& nnOutputs )
{
int mostTopRateIdx = -1;
double mostTopRate = 0;
for ( int i = 0; i < nnOutputs.m_rows; ++i )
{
auto value = nnOutputs.getValue( 0, i );
if ( value > mostTopRate )
{
mostTopRate = value;
mostTopRateIdx = i;
}
}
return mostTopRateIdx;
}
int main()
{
normalizeColors();
NeuralNetwork nn( 3, _countof(g_originalColorData), _countof( g_originalColorData ) );
nn.loadWeights( "weights.txt" );
const auto trainingDataList = createTrainingDataList();
for (;;)
{
nn.train( trainingDataList, 0.05, 5000 );
for ( auto& data : trainingDataList )
{
auto output = nn.foward_prop( data.first );
const int idx = getMostTopIdx( output );
if ( idx >= 0 && idx < _countof( g_originalColorData ) )
{
const int r = static_cast<int>(g_originalColorData[idx].r * 255);
const int g = static_cast<int>(g_originalColorData[idx].g * 255);
const int b = static_cast<int>(g_originalColorData[idx].b * 255);
const int r2 = data.first.getValue( 0, 0 ) * 255;
const int g2 = data.first.getValue( 0, 1 ) * 255;
const int b2 = data.first.getValue( 0, 2 ) * 255;
std::printf( "answer is %d,%d,%d, %s ( correct: %d, %d, %d )\n", r, g, b, g_originalColorData[idx].name, r2, g2, b2 );
}
else
{
std::printf( "idx error\n" );
}
}
nn.saveWeights( "weights.txt" );
system( "PAUSE" );
}
return 0;
}
ちょっと前に作ったjavascript版のニューラルネットワークと同様に、
HTMLカラーコードを学習させることにしました。
xor以外で手ごろな大きさの学習が意外にぱっと出てこないから仕方ないね。
ときどき学習させるとエラーが肥大化していって、最後はdoubleがNaNになって死ぬことがあって、
これが最後まで改善できませんでした:<。
今回はカラーコードのIndexを出力できればいいので、
oneHotを導入することで学習がアバウトでも動作するようにしてみました。
oneHotなしだと途中で学習が停滞してしまうことが多くて、たぶんニューロンが少ないんだろうなと思ってるけど詳細は不明。
そのままでは処理が遅すぎてどうしようもないと思ったので、適当に最適化してみたのがこちら。
[ソースコード]
#include <cstdio>
#include <cmath>
#include <vector>
#include <random>
#include <future>
#include <iostream>
#include <fstream>
#include <string>
class Matrix
{
public:
static Matrix matmul( const Matrix& a, const Matrix& b )
{
Matrix ret( a.m_rows, b.m_cols );
matmul( ret, a, b );
return ret;
}
// outputとa,bが同じアドレスだとうまく動作しない
static void matmul( Matrix& output, const Matrix& a, const Matrix& b )
{
if ( a.m_cols == b.m_rows )
{
output.resize( a.m_rows, b.m_cols );
output.zero();
double* pOutStart = output.m_values.data();
const double* pAStart = a.m_values.data();
const double* pBStart = b.m_values.data();
const int addOutY = output.m_cols;
const int addAY = a.m_cols;
const int addBY = b.m_cols;
const int yMax = output.m_rows;
const int xMax = output.m_cols;
const int zMax = a.m_cols;
for ( int y = 0; y < yMax; y++ )
{
const double* pA = pAStart;
const double* pBStart2 = pBStart;
for ( int z = 0; z < zMax; ++z )
{
double* pOut = pOutStart;
const double* pB = pBStart2;
for ( int x = 0; x < xMax; x++ )
{
*pOut += *pA * *pB;
pOut++;
pB++;
}
pA++;
pBStart2 += addBY;
}
pOutStart += addOutY;
pAStart += addAY;
}
}
else
{
std::printf( "%s - miss match matrix.[%dx%d]x[%dx%d]\n", __FUNCSIG__, a.m_rows, a.m_cols, b.m_rows, b.m_cols );
}
}
// outputとa,bが同じアドレスだとうまく動作しない
// a=normal, b=transposeにして乗算
static void matmul_nt( Matrix& out, const Matrix& a, const Matrix& b )
{
const int b_rows = b.m_cols;
const int b_cols = b.m_rows;
if ( a.m_cols == b_rows )
{
out.resize( a.m_rows, b_cols );
out.zero();
double* pOutStart = out.m_values.data();
const double* pAStart = a.m_values.data();
const double* pBStart = b.m_values.data();
const int addOutY = out.m_cols;
const int addAY = a.m_cols;
const int addBY = b.m_cols;
const int yMax = out.m_rows;
const int xMax = out.m_cols;
const int zMax = a.m_cols;
for ( int y = 0; y < yMax; y++ )
{
const double* pA = pAStart;
const double* pBStart2 = pBStart;
for ( int z = 0; z < zMax; ++z )
{
double* pOut = pOutStart;
const double* pB = pBStart2;
for ( int x = 0; x < xMax; x++ )
{
*pOut += *pA * *pB;
pOut++;
pB+=addBY;
}
pA++;
pBStart2++;
}
pOutStart += addOutY;
pAStart += addAY;
}
}
else
{
std::printf( "%s - miss match matrix.[%dx%d]x[%dx%d]\n", __FUNCSIG__, a.m_rows, a.m_cols, b.m_rows, b.m_cols );
}
}
// outputとa,bが同じアドレスだとうまく動作しない
// a=transpose, b=normalにして乗算
static void matmul_tn( Matrix& out, const Matrix& a, const Matrix& b )
{
const int a_rows = a.m_cols;
const int a_cols = a.m_rows;
if ( a_cols == b.m_rows )
{
out.resize( a_rows, b.m_cols );
out.zero();
double* pOutStart = out.m_values.data();
const double* pAStart = a.m_values.data();
const double* pBStart = b.m_values.data();
const int addOutY = out.m_cols;
const int addAY = a.m_cols;
const int addBY = b.m_cols;
const int yMax = out.m_rows;
const int xMax = out.m_cols;
const int zMax = a_cols;
for ( int y = 0; y < yMax; y++ )
{
const double* pA = pAStart;
const double* pBStart2 = pBStart;
for ( int z = 0; z < zMax; ++z )
{
double* pOut = pOutStart;
const double* pB = pBStart2;
for ( int x = 0; x < xMax; x++ )
{
*pOut += *pA * *pB;
pOut++;
pB += addBY;
}
pA += addAY;
pBStart2++;
}
pOutStart += addOutY;
pAStart++;
}
}
else
{
std::printf( "%s - miss match matrix.[%dx%d]x[%dx%d]\n", __FUNCSIG__, a.m_rows, a.m_cols, b.m_rows, b.m_cols );
}
}
#if 0
static Matrix sub( const Matrix& a, const Matrix& b )
{
Matrix ret( a.m_rows, a.m_cols );
if ( a.m_cols == b.m_cols &&
a.m_rows == b.m_rows )
{
double* pOut = ret.m_values.data();
const double* pA = a.m_values.data();
const double* pB = b.m_values.data();
const int n = ret.size();
for ( int i = 0; i < n; ++i )
{
*pOut = *pA - *pB;
pOut++;
pA++;
pB++;
}
}
else
{
std::printf("%s - not same size[%d,%d]-[%d,%d]\n", __FUNCSIG__, a.m_rows, a.m_cols, b.m_rows, b.m_cols );
}
return ret;
}
#endif
static void sub( Matrix& out, const Matrix& a, const Matrix& b )
{
if ( a.m_cols == b.m_cols &&
a.m_rows == b.m_rows )
{
out.resize( a.m_rows, a.m_cols );
double* pOut = out.m_values.data();
const double* pA = a.m_values.data();
const double* pB = b.m_values.data();
const int n = out.size();
for ( int i = 0; i < n; ++i )
{
*pOut = *pA - *pB;
pOut++;
pA++;
pB++;
}
}
else
{
std::printf( "%s - not same size[%d,%d]-[%d,%d]\n", __FUNCSIG__, a.m_rows, a.m_cols, b.m_rows, b.m_cols );
}
}
static Matrix createFromArray( const std::vector<double>& array )
{
Matrix ret( array.size(), 1 );
const double* pArray = array.data();
const int arraySize = array.size();
for ( int i = 0; i < arraySize; ++i )
{
ret.getValue(0,i) = *pArray;
pArray++;
}
return ret;
}
// 使ってないけど一応残しておく
static Matrix transpose( const Matrix& m )
{
Matrix ret( m.m_cols, m.m_rows );
double* pOutStart = ret.m_values.data();
const double* pInStart = m.m_values.data();
const int addOutY = ret.m_cols;
const int addInY = m.m_cols;
for ( int y = 0; y < ret.m_rows; ++y )
{
double* pOut = pOutStart;
const double* pIn = pInStart;
for ( int x = 0; x < ret.m_cols; ++x )
{
*pOut = *pIn;
pOut++;
pIn += addInY;
}
pOutStart += addOutY;
pInStart++;;
}
return ret;
}
public:
Matrix( int rows, int cols )
: m_rows( rows )
, m_cols( cols )
{
m_values.resize( rows * cols );
}
Matrix( const Matrix& m )
: m_rows( m.m_rows )
, m_cols( m.m_cols )
, m_values( m.m_values )
{
}
Matrix& operator = ( const Matrix& m )
{
resize( m.m_rows, m.m_cols );
memcpy( m_values.data(), m.m_values.data(), m.size() * sizeof( double ) );
return *this;
}
void resize( int rows, int cols )
{
if ( m_rows != rows ||
m_cols != cols )
{
if ( m_values.size() < rows * cols )
{
m_values.resize( rows * cols );
}
m_rows = rows;
m_cols = cols;
}
}
double& getValue( int x, int y )
{
return m_values[y * m_cols + x];
}
const double& getValue( int x, int y ) const
{
return m_values[y * m_cols + x];
}
void randomizeN()
{
std::random_device rndDev;
std::mt19937 random( rndDev() );
//std::mt19937 random( 0 );
std::uniform_real_distribution<double> dist( 0, 1.0 / std::sqrt(static_cast<double>(size())) );
for ( auto& v : m_values )
{
v = dist( random );
}
}
void multiplyElementwise( const Matrix& m )
{
if ( m_rows != m.m_rows || m_cols != m.m_cols )
{
std::printf( "%s - not same size[%d,%d]-[%d,%d]\n", __FUNCSIG__, m_rows, m_cols, m.m_rows, m.m_cols );
return;
}
double* pOut = m_values.data();
const double* pIn = m.m_values.data();
const int n = size();
for ( int i = 0; i < n; ++i )
{
*pOut *= *pIn;
pOut++;
pIn++;
}
}
void multiplyScalar( double d )
{
double* pOut = m_values.data();
int n = size();
for ( int i = 0; i < n; ++i )
{
*pOut *= d;
pOut++;
}
}
void add( double d )
{
double* pOut = m_values.data();
int n = size();
for ( int i = 0; i < n; ++i )
{
*pOut += d;
pOut++;
}
}
void add( const Matrix& m )
{
if ( m_rows != m.m_rows || m_cols != m.m_cols )
{
std::printf( "%s - not same size[%d,%d]-[%d,%d]\n", __FUNCSIG__, m_rows, m_cols, m.m_rows, m.m_cols );
return;
}
double* pOut = m_values.data();
const double* pIn = m.m_values.data();
int n = size();
for ( int i = 0; i < n; ++i )
{
*pOut += *pIn;
pOut++;
pIn++;
}
}
Matrix& operator + ( const Matrix& m )
{
add( m );
return *this;
}
void map( std::function<double( const double& )> fn )
{
double* pOut = m_values.data();
const int n = size();
for ( int i = 0; i < n; ++i )
{
*pOut = fn( *pOut );
pOut++;
}
}
void print() const
{
std::printf( "%s - rows: %d, cols: %d\n", __FUNCSIG__, m_rows, m_cols );
for ( int y = 0; y < m_rows; ++y )
{
for ( int x = 0; x < m_cols; ++x )
{
std::printf( "%f, ", getValue(x,y) );
}
std::printf( "\n" );
}
}
void zero()
{
memset( m_values.data(), 0, m_values.size() * sizeof( double ) );
}
int size() const
{
return m_rows * m_cols;
}
int m_rows;
int m_cols;
std::vector<double> m_values;
};
class NeuralNetwork
{
public:
static double sigmoid( const double& d )
{
return 1.0 / (1.0 + std::exp( -d ));
}
static double leaklyRelu( const double& d )
{
return std::max( d * 0.01, d );
//return std::min( std::max( d * 0.01, d ), 6.0 );
}
static double dsigmoid( const double& d )
{
//const double s = sigmoid( d );
//return s * (1 - s);
return d * (1.0 - d);
}
static double dleaklyRelu( const double& d )
{
return (d >= 0.0) ? 1.0 : 0.01;
}
static double tanh( const double& d )
{
return std::tanh( d );
}
static double dtanh( const double& d )
{
return 1.0 - std::pow( std::tanh( d ), 2.0 );
}
public:
NeuralNetwork( int input, int hidden, int output )
{
create( input, hidden, output );
}
NeuralNetwork( int input, int hidden, int hidden2, int output )
{
create( input, hidden, hidden2, output );
}
void create( int input, int hidden, int output )
{
m_weights.push_back( Matrix( hidden, input ) );
m_weights.push_back( Matrix( output, hidden ) );
for ( auto& m : m_weights )
{
m.randomizeN();
}
m_bias.push_back( Matrix( hidden, 1 ) );
m_bias.push_back( Matrix( output, 1 ) );
for ( auto& m : m_bias )
{
m.randomizeN();
}
m_output.resize( m_bias.size(), Matrix(output, 1) );
}
void create( int input, int hidden, int hidden2, int output )
{
m_weights.push_back( Matrix( hidden, input ) );
m_weights.push_back( Matrix( hidden2, hidden ) );
m_weights.push_back( Matrix( output, hidden2 ) );
for ( auto& m : m_weights )
{
m.randomizeN();
}
m_bias.push_back( Matrix( hidden, 1 ) );
m_bias.push_back( Matrix( hidden2, 1 ) );
m_bias.push_back( Matrix( output, 1 ) );
for ( auto& m : m_bias )
{
m.randomizeN();
}
m_output.resize( m_bias.size(), Matrix( output, 1 ) );
}
Matrix foward_prop( const Matrix& input )
{
Matrix* pM = const_cast<Matrix*>(&input);
Matrix* pOut = m_output.data();
Matrix* pWeights = m_weights.data();
Matrix* pBias = m_bias.data();
const int n = m_weights.size();
for ( int i = 0; i < n; ++i )
{
Matrix::matmul( *pOut, *pWeights, *pM );
pOut->add( *pBias );
pOut->map( leaklyRelu );
pM = pOut;
pOut++;
pWeights++;
pBias++;
}
oneHot( *pM );
return *pM;
}
void step( Matrix& m )
{
for ( int i = 0; i < m.size(); ++i )
{
m.m_values[i] = m.m_values[i] > 0.5 ? 1 : 0;
}
}
void oneHot( Matrix& m )
{
int mostTopIdx = -1;
double mostTopValue = -DBL_MIN;
for ( int i = 0; i < m.size(); ++i )
{
double& d = m.m_values[i];
if ( d > mostTopValue )
{
mostTopValue = d;
mostTopIdx = i;
}
d = 0;
}
if ( mostTopIdx >= 0 && mostTopIdx < m.size() )
{
m.m_values[mostTopIdx] = 1;
}
else
{
std::printf("%s - err. couldnt find most top idx\n", __FUNCSIG__);
}
}
// [参考]part4
void train( const std::vector<std::pair<Matrix, Matrix>>& trainingDataList, double l_rate, int epoch )
{
// メモリ確保を防ぐために外にだしておいて、メモリ確保回数を最小限にしてみる
Matrix dOutput( 1, 1 );
Matrix prevError( 1, 1 );
Matrix gradients( 1, 1 );
Matrix weightGrad( 1, 1 );
Matrix err( 1, 1 );
// 学習をランダムにしてみる
auto trainingDataListShuffle = trainingDataList;
std::random_device randDev;
std::mt19937 randMt( randDev() );
//std::mt19937 randMt( 0 );
for ( int e = 0; e < epoch; ++e )
{
std::shuffle( trainingDataListShuffle.begin(), trainingDataListShuffle.end(), randMt );
double sumError = 0;
for ( auto& trainingData : trainingDataListShuffle )
{
// forward
foward_prop( trainingData.first );
// calc error
Matrix::sub( err, trainingData.second, m_output.back() );
// sumError
for ( int i = 0; i < err.m_rows; ++i )
{
const float d = err.getValue( 0, i );
sumError += d * d;
}
// training
for ( int i=m_weights.size()-1; i>=0; i--)
{
Matrix::matmul_tn( prevError, m_weights[i], err ); //< 前のレイヤー(次の処理)へ渡すerr
dOutput = m_output[i];
dOutput.map( dleaklyRelu );
/*
Matrix& gradients = err; //< これが地味に重い。わかりやすくするために名前だけ変えておきたかったが
gradients.multiplyElementwise( dOutput );
gradients.multiplyScalar( l_rate );
Matrix::matmul_nt( weightGrad, gradients, i > 0 ? m_output[i - 1] : trainingData.first );
m_bias[i].add( gradients );
m_weights[i].add( weightGrad );
*/
err.multiplyElementwise( dOutput );
err.multiplyScalar( l_rate );
Matrix::matmul_nt( weightGrad, err, i > 0 ? m_output[i - 1] : trainingData.first );
m_bias[i].add( err );
m_weights[i].add( weightGrad );
err = prevError;
}
}
std::printf("[%d] %f\n", e, sumError);
}
}
void print()
{
std::printf( "%s\n", __FUNCSIG__ );
std::printf( "weights\n" );
for ( int i = 0; i < m_weights.size(); ++i )
{
const Matrix& m = m_weights[i];
for ( int y = 0; y < m.m_rows; ++y )
{
for ( int x = 0; x < m.m_cols; ++x )
{
std::printf( "[layer:%d][%d][%d]%f\n", i, y, x, m.getValue(x,y) );
}
}
}
std::printf( "bias\n" );
for ( int i = 0; i < m_bias.size(); ++i )
{
const Matrix& m = m_bias[i];
for ( int y = 0; y < m.m_rows; ++y )
{
for ( int x = 0; x < m.m_cols; ++x )
{
std::printf( "[layer:%d][%d][%d]%f\n", i, y, x, m.getValue(x,y) );
}
}
}
}
bool saveWeights( char* fileName ) const
{
std::ofstream outputFile( fileName );
if ( outputFile.is_open() )
{
// save weights
for ( auto& m : m_weights )
{
for ( auto& d : m.m_values )
{
auto weightStr = std::to_string( d );
outputFile << weightStr << ",";
}
}
// save bias
for ( auto& m : m_bias )
{
for ( auto& d : m.m_values )
{
auto weightStr = std::to_string( d );
outputFile << weightStr << ",";
}
}
outputFile.close();
return true;
}
return false;
}
bool loadWeights( char* fileName )
{
if ( fileName )
{
std::vector<double> weightsList;
// read weights file
{
std::ifstream inputFile( fileName );
if ( inputFile.is_open() )
{
std::string str;
while ( std::getline( inputFile, str, ',' ) )
{
double d = std::stod( str );
weightsList.push_back( d );
}
}
inputFile.close();
}
if ( weightsList.empty() ) return false;
// update weights
int idx = 0;
for ( auto& m : m_weights )
{
for ( auto& d : m.m_values )
{
if ( weightsList.size() <= idx ) break;
d = weightsList[idx++];
}
}
// update bias
for ( auto& m : m_bias )
{
for ( auto& d : m.m_values )
{
if ( weightsList.size() <= idx ) break;
d = weightsList[idx++];
}
}
return true;
}
return false;
}
//private:
public:
std::vector<Matrix> m_weights;
std::vector<Matrix> m_bias;
std::vector<Matrix> m_output;
};
// [参考]http://www.htmq.com/color/colorname.shtml
// 140 color
struct OriginalColorData
{
double r, g, b;
char* name;
} g_originalColorData[] = {
{ 255, 255, 255, "white" },
{ 245,245,245, "whitesmoke" },
{ 248,248,255, "ghostwhite" },
{ 240,248,255, "aliceblue" },
{ 230,230,250, "lavender" },
{ 240,255,255, "azure" },
{ 224,255,255, "lightcyan" },
{ 245,255,250, "mintcream" },
{ 240,255,240, "honeydew" },
{ 255,255,240, "ivory" },
{ 245,245,220, "beige" },
{ 255,255,224, "lightyellow" },
{ 250,250,210, "lightgoldenrodyellow" },
{ 255,250,205, "lemonchiffon" },
{ 255,250,240, "floralwhite" },
{ 253,245,230, "oldlace" },
{ 255,248,220, "cornsilk" },
{ 255,239,213, "papayawhite" },
{ 255,235,205, "blanchedalmond" },
{ 255,228,196, "bisque" },
{ 255,250,250, "snow" },
{ 250,240,230, "linen" },
{ 250,235,215, "antiquewhite" },
{ 255,245,238, "seashell" },
{ 255,240,245, "lavenderblush" },
{ 255,228,225, "mistyrose" },
{ 220,220,220, "gainsboro" },
{ 211,211,211, "lightgray" },
{ 176,196,222, "lightsteelblue" },
{ 173,216,230, "lightblue" },
{ 135,206,250, "lightskyblue" },
{ 176,224,230, "powderblue" },
{ 175,238,238, "paleturquoise" },
{ 135,206,235, "skyblue" },
{ 102,205,170, "mediumaquamarine" },
{ 127,255,212, "aquamarine" },
{ 152,251,152, "palegreen" },
{ 144,238,144, "lightgreen" },
{ 240,230,140, "khaki" },
{ 238,232,170, "palegoldenrod" },
{ 255,228,181, "moccasin" },
{ 255,222,173, "navajowhite" },
{ 255,218,185, "peachpuff" },
{ 245,222,179, "wheat" },
{ 255,192,203, "pink" },
{ 255,182,193, "lightpink" },
{ 216,191,216, "thistle" },
{ 221,160,221, "plum" },
{ 192,192,192, "silver" },
{ 169,169,169, "darkgray" },
{ 119,136,153, "lightslategray" },
{ 112,128,144, "slategray" },
{ 106,90,205, "slateblue" },
//*
{ 70,130,180, "steelblue" },
{ 123,104,238, "mediumslateblue" },
{ 65,105,225, "royalblue" },
{ 0,0,255, "blue" },
{ 30,144,255, "dodgerblue" },
{ 100,149,237, "cornflowerblue" },
{ 0,191,255, "deepskyblue" },
{ 0,255,255, "cyan(aqua)" },
{ 64,224,208, "turquoise" },
{ 72,209,204, "mediumturquoise" },
{ 0,206,209, "darkturquoise" },
{ 32,178,170, "lightseagreen" },
{ 0,250,154, "mediumspringgreen" },
{ 0,255,127, "springgreen" },
{ 0,255,0, "lime" },
{ 50,205,50, "limegreen" },
{ 154,205,50, "yellowgreen" },
{ 124,252,0, "lawngreen" },
{ 127,255,0, "chartreuse" },
{ 173,255,47, "greenyellow" },
{ 255,255,0, "yellow" },
{ 255,215,0, "gold" },
{ 255,165,0, "orange" },
{ 255,140,0, "darkorange" },
{ 218,165,32, "goldenrod" },
{ 222,184,135, "burlywood" },
{ 210,180,140, "tan" },
{ 244,164,96, "sandybrown" },
{ 233,150,122, "darksalmon" },
{ 240,128,128, "lightcoral" },
{ 250,128,114, "salmon" },
{ 255,160,122, "lightsalmon" },
{ 255,127,80, "coral" },
{ 255,99,71, "tomato" },
{ 255,69,0, "orangered" },
{ 255,0,0, "red" },
{ 255,20,147, "deeppink" },
{ 255,105,180, "hotpink" },
{ 219,112,147, "palevioletred" },
{ 238,130,238, "violet" },
{ 218,112,214, "orchid" },
{ 255,0,255, "magenta(fuchsia)" },
{ 186,85,211, "mediumorchid" },
{ 153,50,204, "darkorchid" },
{ 148,0,211, "darkviolet" },
{ 138,43,226, "blueviolet" },
{ 147,112,219, "mediumpurple" },
{ 128,128,128, "gray" },
{ 0,0,205, "mediumblue" },
{ 0,139,139, "darkcyan" },
{ 95,158,160, "cadetblue" },
{ 143,188,143, "darkseagreen" },
{ 60,179,113, "mediumseagreen" },
{ 0,128,128, "teal" },
{ 34,139,34, "forestgreen" },
{ 46,139,87, "seagreen" },
{ 189,183,107, "darkkhaki" },
{ 205,133,63, "peru" },
{ 220,20,60, "crimson" },
{ 205,92,92, "indianred" },
{ 188,143,143, "rosybrown" },
{ 199,21,133, "mediumvioletred" },
{ 105,105,105, "dimgray" },
{ 0,0,0, "black" },
{ 25,25,112, "midnightblue" },
{ 72,61,139, "darkslateblue" },
{ 0,0,139, "darkblue" },
{ 0,0,128, "navy" },
{ 47,79,79, "darkslategray" },
{ 0,128,0, "green" },
{ 0,100,0, "darkgreen" },
{ 85,107,47, "darkolivegreen" },
{ 107,142,35, "olivedrab" },
{ 128,128,0, "olive" },
{ 184,134,11, "darkgoldenrod" },
{ 210,105,30, "chocolate" },
{ 160,82,45, "sienna" },
{ 139,69,19, "saddlebrown" },
{ 178,34,34, "firebrick" },
{ 165,42,42, "brown" },
{ 128,0,0, "maroon" },
{ 139,0,0, "darkred" },
{ 139,0,139, "darkmagenta" },
{ 128,0,128, "purple" },
{ 75,0,130, "indigo" },
//*/
};
static void normalizeColors()
{
for ( auto& data : g_originalColorData )
{
data.r /= 255;
data.g /= 255;
data.b /= 255;
}
}
static std::vector<std::pair<Matrix, Matrix>> createTrainingDataList()
{
std::vector< std::pair<Matrix, Matrix> > ret;
for ( int i = 0; i < _countof( g_originalColorData ); ++i )
{
// input
std::pair<Matrix, Matrix> data( Matrix( 1, 1 ), Matrix( 1, 1 ) );
data.first = Matrix::createFromArray( std::vector<double>( { g_originalColorData[i].r, g_originalColorData[i].g, g_originalColorData[i].b } ) );
// output
std::vector<double> tmp( _countof( g_originalColorData ) );
tmp[i] = 1;
data.second = Matrix::createFromArray( tmp );
ret.push_back( data );
}
return ret;
}
static int getMostTopIdx( const Matrix& nnOutputs )
{
int mostTopRateIdx = -1;
double mostTopRate = 0;
for ( int i = 0; i < nnOutputs.m_rows; ++i )
{
//auto value = nnOutputs.m_values[i][0];
auto value = nnOutputs.getValue( 0, i );
if ( value > mostTopRate )
{
mostTopRate = value;
mostTopRateIdx = i;
}
}
return mostTopRateIdx;
}
int main()
{
normalizeColors();
NeuralNetwork nn( 3, _countof(g_originalColorData), _countof( g_originalColorData ) );
nn.loadWeights( "weights.txt" );
const auto trainingDataList = createTrainingDataList();
for (;;)
{
nn.train( trainingDataList, 0.05, 5000 );
for ( auto& data : trainingDataList )
{
auto output = nn.foward_prop( data.first );
//output.print();
const int idx = getMostTopIdx( output );
if ( idx >= 0 && idx < _countof( g_originalColorData ) )
{
const int r = static_cast<int>(g_originalColorData[idx].r * 255);
const int g = static_cast<int>(g_originalColorData[idx].g * 255);
const int b = static_cast<int>(g_originalColorData[idx].b * 255);
const int r2 = data.first.getValue( 0, 0 ) * 255;
const int g2 = data.first.getValue( 0, 1 ) * 255;
const int b2 = data.first.getValue( 0, 2 ) * 255;
std::printf( "answer is %d,%d,%d, %s ( correct: %d, %d, %d )\n", r, g, b, g_originalColorData[idx].name, r2, g2, b2 );
}
else
{
std::printf( "idx error\n" );
}
}
//nn.print();
nn.saveWeights( "weights.txt" );
system( "PAUSE" );
}
return 0;
}
最適化の方針としては、
・Matrixを二次元配列から一次元配列にしてキャッシュに乗りやすく。これは参考サイトさんほぼそのままです。
・行列の乗算時のアドレス計算を、ポインタを使用して掛け算をできるだけ消しました。乗除算は基本的に重いので繰り返し処理の内側ではできるだけ減らしたい。
・行列の乗算を、計算順序を変えてキャッシュに乗りやすいらしい順序に変更。
static Matrix matmul( const Matrix& a, const Matrix& b )
{
Matrix ret( a.m_rows, b.m_cols );
if ( a.m_cols == b.m_rows )
{
for ( int y = 0; y < ret.m_rows; y++ )
{
for ( int z = 0; z < a.m_cols; ++z )
{
for ( int x = 0; x < ret.m_cols; x++ )
{
ret.getValue( x, y ) += a.getValue( z, y ) * b.getValue( x, z );
}
}
}
}
return ret;
}
zとxの順番が逆に。これをポインタでぐちゃぐちゃにしたのが上のになります。実際に動作させてみると確かにちょっと早くなった。・transpose処理いらなくない?
と思って専用の乗算処理を用意してみました。乗算処理内でアドレス計算を縦横入れ替えています。
・行列用のメモリを確保するの重いので、メモリが足りていればメモリ確保しないようにして一時変数として使いまわすようにしています。
最終的には既存のライブラリを使うほうが早いし汎用性もあるということになるので、深入りはやめました。
GPU使うなり、マルチスレッドを使うなりあると思います。
2022年11月3日木曜日
指定の色に近いカラーネームを取得する。
■指定の色に近いカラーネームを取得する。(要javascript)
使い方:バーを動かして色を指定すると、下に結果が表示されます。
ニューラルネットワークは確率の高いものを上から順番に、
プログラムで算出したものは、パラメータが近いものを上から順に表示しています。
■
R: 128
G: 128
B: 128
■ニューラルネットワークが算出した近い色(上位5つ)
*
■普通にプログラムで算出した近い色(上位5つ)
*
■あえてニューラルネットワークの記憶を消す
[記憶を消す]ボタンで、ニューラルネットワークを無学習状態にできます。
[training]ボタンを押して学習させていくことで、各カラーネームのパラメータを覚えていきます。
[学習結果をみる]ボタンで各カラーネームをどれくらい記憶したかを確認できます。
ページをリロードすると元に戻せますのでお気軽にどうぞ。
学習回数:
学習率:
- 学習にすごい時間がかかるので注意。
*
使い方:バーを動かして色を指定すると、下に結果が表示されます。
ニューラルネットワークは確率の高いものを上から順番に、
プログラムで算出したものは、パラメータが近いものを上から順に表示しています。
■
R: 128
G: 128
B: 128
■ニューラルネットワークが算出した近い色(上位5つ)
*
■普通にプログラムで算出した近い色(上位5つ)
*
■あえてニューラルネットワークの記憶を消す
[記憶を消す]ボタンで、ニューラルネットワークを無学習状態にできます。
[training]ボタンを押して学習させていくことで、各カラーネームのパラメータを覚えていきます。
[学習結果をみる]ボタンで各カラーネームをどれくらい記憶したかを確認できます。
ページをリロードすると元に戻せますのでお気軽にどうぞ。
学習回数:
学習率:
- 学習にすごい時間がかかるので注意。
*
登録:
コメント (Atom)



