今回の目標
行列計算用のクラスが必要だと思ったので作ります。
ここから本編
現在のディレクトリ構成
現在のディレクトリ構成は以下の通りです。
ファイルは省略しています。
MyNet
├── layer // 層のパッケージ(今は空)
├── matrix // 行列のパッケージ(今回作成)
├── nodes // ノードのパッケージ(前回作成)
│ └── out_function // 活性化関数のパッケージ(前回作成)
└── optimzer // 最適化関数のパッケージ(今は空)
メンバ変数とコンストラクタ
できるだけnumpyのndarrayに近い形にしたいと考えています。
演算子のオーバーライドができない時点で不可能ではありますが、できるだけ近い感覚で使いたいです。
なんとなくいろんな引数に対応できるようにしました。実際こんなに使うかは分かりませんが・・・。
/**
* Class for calculation and save of matrix.
*/
public class Matrix{
/** Matrix's value. */
public double matrix[][];
/** Matrix's row, col. */
public int row, col;
/** Matrix's shape */
public int shape[];
/**
* Constructor for this class.
* Set the matrix and its size.
* @param in Two dimensional matrix of type Matrix.
*/
public Matrix(Matrix in){
this.matrix = new double[in.row][in.col];
this.shape = new int[2];
this.row = in.row;
this.col = in.col;
this.shape[0] = this.row; this.shape[1] = this.col;
for (int i = 0; i < this.row; i++){
for (int j = 0; j < this.col; j++){
this.matrix[i][j] = in.matrix[i][j];
}
}
}
/**
* Constructor for this class.
* Set the matrix and its size.
* @param in Two dimensional matrix of type double[][].
*/
public Matrix(double in[][]){
this.matrix = new double[in.length][in[0].length];
this.shape = new int[2];
this.row = in.length;
this.col = in[0].length;
this.shape[0] = this.row; this.shape[1] = this.col;
for (int i = 0; i < this.row; i++){
for (int j = 0; j < this.col; j++){
this.matrix[i][j] = in[i][j];
}
}
}
/**
* Constructor for this class.
* Set the matrix and its size.
* @param in Two dimensional matrix of type Double[][].
*/
public Matrix(Double in[][]){
this.matrix = new double[in.length][in[0].length];
this.shape = new int[2];
this.row = in.length;
this.col = in[0].length;
this.shape[0] = this.row; this.shape[1] = this.col;
for (int i = 0; i < this.row; i++){
for (int j = 0; j < this.col; j++){
this.matrix[i][j] = in[i][j];
}
}
}
足し算
行列に行列を足す、行列に数字を足す、自分が持つ行列に行列を足す、自分が持つ行列に数字を足すの四種類を実装しました。
/**
* Add a matrix to a matrix.
* @param a Augend matrix.
* @param b Addend matrix.
* @return New added Matrix instance.
*/
public static Matrix add(Matrix a, Matrix b){
if (a.row != b.row || a.col != b.col){
System.out.println("adding error");
System.exit(-1);
}
Matrix rtn = new Matrix(new double[a.row][a.col]);
for (int i = 0; i < a.row; i++){
for (int j = 0; j < a.col; j++){
rtn.matrix[i][j] = a.matrix[i][j] + b.matrix[i][j];
}
}
return rtn;
}
/**
* Add a number to a matirx.
* @param a Matrix.
* @param num Number.
* @return New added Matrix instance.
*/
public static Matrix add(Matrix a, double num){
Matrix rtn = new Matrix(new double[a.row][a.col]);
for (int i = 0; i < a.row; i++){
for (int j = 0; j < a.col; j++){
rtn.matrix[i][j] = a.matrix[i][j] + num;
}
}
return rtn;
}
/**
* Add a matrix to this matrix.
* @param a Addend matrix.
*/
public void add(Matrix a){
if (this.row != a.row || this.col != a.col){
System.out.println("adding error");
System.exit(-1);
}
for (int i = 0; i < this.row; i++){
for (int j = 0; j < this.col; j++){
this.matrix[i][j] += a.matrix[i][j];
}
}
}
/**
* Add a number to this matirx.
* @param a Matrix.
* @return New added Matrix instance.
*/
public void add(double num){
Matrix rtn = new Matrix(new double[this.row][this.col]);
for (int i = 0; i < this.row; i++){
for (int j = 0; j < this.col; j++){
rtn.matrix[i][j] = this.matrix[i][j] + num;
}
}
}
引き算
足し算と違い、自分の行列に対して作用するようなものは作りませんでした。
理由として、引く数と引かれる数の順番が変わると違う結果になるからです。引き算するときは、-1倍して足すなど工夫が必要になりそうです。
プログラムは足し算とほぼ同じなので省略します。
掛け算
数字との掛け算を、静的動的療法用意しました。
/**
* Multiply a matrix by a number.
* @param a Matrix.
* @param num Number.
* @return New multiplied Matrix instance.
*/
public static Matrix mult(Matrix a, double num){
Matrix rtn = new Matrix(new double[a.row][a.col]);
for (int i = 0; i < a.row; i++){
for (int j = 0; j < a.col; j++){
rtn.matrix[i][j] = a.matrix[i][j] * num;
}
}
return rtn;
}
/**
* Multiply this matrix by a number.
* @param num Number.
*/
public void mult(double num){
Matrix rtn = new Matrix(new double[this.row][this.col]);
for (int i = 0; i < this.row; i++){
for (int j = 0; j < this.col; j++){
rtn.matrix[i][j] = this.matrix[i][j] * num;
}
}
}
割り算
行列を数字で割るのは順番なんてないので、掛け算と同様二種類用意しました。
掛け算とほぼ同じプログラムなので省略。
内積
内積計算です。
特筆すべきことはなし。
/**
* Dot product for two matrices.
* @param a Matrix to be dot producted.
* @param b Matrix to dot product.
* @return New dot producted Matrix instance.
*/
public static Matrix dot(Matrix a, Matrix b){
if (a.col != b.row){
System.out.println("dot producting error");
System.exit(-1);
}
Matrix rtn = new Matrix(new double[a.row][b.col]);
double num = 0;
for (int i = 0; i < a.row; i++){
for (int j = 0; j < b.col; j++){
num = 0;
for (int k = 0; k < a.col; k++){
num += a.matrix[i][j] * b.matrix[j][i];
}
rtn.matrix[i][j] = num;
}
}
return rtn;
}
転置行列
内積と同様、特筆すべきことはなし。
/**
* Create transpose of this matrix.
* @return New Matrix instance transposed of this matrix .
*/
public Matrix T(){
Matrix rtn = new Matrix(new double[col][row]);
for (int i = 0; i < this.row; i++){
for (int j = 0; j < this.col; j++){
rtn.matrix[j][i] = this.matrix[i][j];
}
}
return rtn;
}
toString
せっかくなのでndarrayっぽく表示されるようにしました。
@Override
public String toString(){
String str = "[";
int i = 0;
for (double[] ele: matrix){
if (i == 0){
str += "[";
}else{
str += "\n [";
}
i++;
for (double num: ele){
str += String.format("%.4f ", num);
}
str += "]";
}
str += "]\n";
return str;
}
equals
Matrixオブジェクトだけでなく、double[][]、Double[][]とも比較できるようにしました。使うかは分かりませんが。
シンプルに全要素が同じなら同一判定です。
/**
* Method to compare this Matrix instance and a Matrix instance.
* Without override.
* @param o A Matrix instance.
* @return Is equal?
*/
public boolean equals(Matrix o){
if (o == this){
return true;
}
if (this.row != o.row || this.col != o.col){
return false;
}
for (int i = 0; i < this.row; i++){
for (int j = 0; j < this.col; j++){
if (this.matrix[i][j] != o.matrix[i][j]){
return false;
}
}
}
return true;
}
/**
* Method to compare this Matrix instance and a double[][] instance.
* Without override.
* @param o A double[][] instance.
* @return Is equal?
*/
public boolean equals(double o[][]){
if (this.row != o.length || this.col != o[0].length){
return false;
}
for (int i = 0; i < o.length; i++){
for (int j = 0; j < o[0].length; j++){
if (this.matrix[i][j] != o[i][j]){
return false;
}
}
}
return true;
}
/**
* Method to compare this Matrix instance and a double[][] instance.
* Without override.
* @param o A Double[][] instance.
* @return Is equal?
*/
public boolean equals(Double o[][]){
if (this.row != o.length || this.col != o[0].length){
return false;
}
for (int i = 0; i < o.length; i++){
for (int j = 0; j < o[0].length; j++){
if (this.matrix[i][j] != o[i][j]){
return false;
}
}
}
return true;
}
clone
特筆すべきことはなし。
@Override
public Matrix clone(){
Matrix rtn = new Matrix(new double[this.row][this.col]);
for (int i = 0; i < this.row; i++){
for (int j = 0; j < this.col; j++){
rtn.matrix[i][j] = this.matrix[i][j];
}
}
return rtn;
}
hashCode
合計求めてintにしてます。
@Override
public int hashCode(){
double sum = 0;
for (double[] ele: this.matrix){
for (double num: ele){
sum += num;
}
}
return (int)sum;
}
次回は
層作ります。