barus's diary

とても真面目なblogですにゃ

C++とPythonで行列の転置 for VC++ VS2017

Pythonで書かれたニューラルネットワーク

サンプルコード(Network.py)前々回の記事と、前回の記事から

VC++に書き起こしする際に

ニューラルネットワークの逆伝播する際に行列の転置がある。

 

nabla_w[-1] = np.dot(delta, activations[-2].transpose())

 

今回は、行列の転置について紹介します。

 

転置とは、行列の行と列を入れ替えのことを言う。

f:id:hatakeka:20170706132450p:plain

 

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())

 

これを実行すると

f:id:hatakeka:20170706134129p:plain

 

 

 となる。

正方行列の場合だけでなく、

行と列の長さが異なる場合も考慮しなければなりません。

 

つまり、以下の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");
	}

}



    

これを実行すると以下のような感じとなります。

f:id:hatakeka:20170706130045p:plain

 

 ポイントはベクター型で定義した定数に

std::vector::resize()を用いて、事前にメモリーを確保していることです。

間違いやすいのは、std::vector::reserve()です。

こちらはpush_back()で要素を足す場合なのでresize()を用います。

返還後の箱を用意出来たら。

 int d = v[i][j];
 int x = p % col;
 int y = p / col;

 

 

にて、行列の何番目の何列目かを計算して入れています。
その際に、%と/で、行列を求めていることに注目して下さい。

 

もしかしたら、

転置の仕方で他に早い方法があるかもしれませんが

とりあえずこれで。。。。( ゚Д゚)

 

以上です。