C++とPythonで行列の転置 for VC++ VS2017
Pythonで書かれたニューラルネットワークの
サンプルコード(Network.py)前々回の記事と、前回の記事から
VC++に書き起こしする際に
ニューラルネットワークの逆伝播する際に行列の転置がある。
nabla_w[-1] = np.dot(delta, activations[-2].transpose())
今回は、行列の転置について紹介します。
転置とは、行列の行と列を入れ替えのことを言う。
Pythonの場合の転置は以下のような感じ
import numpy as np
# 4x4のアレイを作成
arr1 = np.arange(16).reshape((4,4))
# 4x4のアレイを作成
arr2 = np.arange(15).reshape((3,5))
# 4x4のアレイを作成
arr3 = np.arange(15).reshape((5,3))
# .Tで転置したアレイを出力(arr自体は書き換わらない)
print("arr1 == \n", arr1)
print("arr1.transpose() == \n", arr1.transpose())
print("arr2 == \n", arr2)
print("arr2.transpose() == \n", arr2.transpose())
print("arr3 == \n", arr3)
print("arr3.transpose() == \n", arr3.transpose())
これを実行すると
となる。
正方行列の場合だけでなく、
行と列の長さが異なる場合も考慮しなければなりません。
つまり、以下の3つのパターンについて転置を行う。
①正方行列(行=列)
②3x5(行<列)
③5x3(行>列)
Pythonで書かれたコードからC++に書き起こしする際には
ニューラルネットワークの隠れ層の指定によっては、
上記の①~③のいずれにも対応していないとエラーとなります。
(私はここではまりました。)
VC++の場合は以下のような感じのコードとなります。
// tennchi.cpp : コンソール アプリケーションのエントリ ポイントを定義します。 // #include "stdafx.h" #include <stdlib.h> #include <vector> #include <stdio.h> #include <iostream> using namespace std; void test(); vector<vector <int>> transpose(vector<vector<int>> v); void show(vector<vector<int>> ary); int main(void) { test(); } void test() { vector<vector<int>> av, bv, cv, rlt; av.resize(3); for (int i = 0; 3 > i; i++)av[i].resize(3); bv.resize(3); for (int i = 0; 3 > i; i++)bv[i].resize(5); cv.resize(5); for (int i = 0; 5 > i; i++)cv[i].resize(3); //4x4正方行列(行と列が同じ) av = { { 41, 42, 43, 44 }, { 45, 46, 47, 48 }, { 49, 50, 51, 52 }, { 53, 54, 55, 56 }}; //3x5行列(列が行より長い) bv = {{ 1, 2, 3, 4, 5 }, { 6, 7, 8, 9, 10 }, { 11, 12, 13, 14, 15 }}; //5x3行列(行が列より長い) cv= {{ 21, 22, 23 }, { 24, 25, 26 }, { 27, 28, 29 }, { 30, 31, 32 }, { 33, 34, 35 }}; show(av); rlt = transpose(av); show(bv); rlt = transpose(bv); show(cv); rlt = transpose(cv); } vector<vector <int>> transpose(vector<vector<int>> v) { int p = 0; vector<vector <int>> rlt; int row = v.size(); int col = v[0].size(); rlt.resize(col); for (int i = 0; col > i; i++)rlt[i].resize(row); printf("transpose.. row=%d, col=%d to row=%d, col=%d\n", row,col,col,row); for (int i = 0; row>i; i++) for (int j = 0; col > j; j++) { int d = v[i][j]; int x = p % col; int y = p / col; rlt[x][y] = d; //printf("p=%d [%02d][%02d]\n", p, x, y); p++; } show(rlt); cout << "done.\n" << endl; return rlt; } void show(vector<vector<int>> ary) { for (int i = 0; ary.size() > i; i++) { for (int j = 0; ary[i].size() > j; j++) printf("%d,", ary[i][j]); printf("\n"); } }
これを実行すると以下のような感じとなります。
ポイントはベクター型で定義した定数に
std::vector::resize()を用いて、事前にメモリーを確保していることです。
間違いやすいのは、std::vector::reserve()です。
こちらはpush_back()で要素を足す場合なのでresize()を用います。
返還後の箱を用意出来たら。
int d = v[i][j]; int x = p % col; int y = p / col;
にて、行列の何番目の何列目かを計算して入れています。
その際に、%と/で、行列を求めていることに注目して下さい。
もしかしたら、
転置の仕方で他に早い方法があるかもしれませんが
とりあえずこれで。。。。( ゚Д゚)
以上です。