LoginSignup
3
1

More than 3 years have passed since last update.

Problem To Artificial Intelligence ~AIを作るAI~

Last updated at Posted at 2019-07-22

はじめに

本記事において生成するAIはLinear Regression Modelを想定します.
複雑なNeural NetworkやTree Decision Modelを想像してページを開いた方はごめんなさい.
(Neural Architecture Searchの記事でもないです)

AIをつくるAIとは?

通常のAI(機械学習)は問題($X, y \in P$)が与えられたときに,$y=f(X)$となるような関数$f$を見つけ出します.
このAIの入力は$X$であり,出力は$y$です.

problemToAI.jpg

一方で,AIをつくるAIとは問題が入力されたときに,AIを出力するAIのことを指します.

problemToAI (1).jpg

ちょっとややこしいので,AIをつくるAIをMeta AI,作られるAIを単にAIと呼びます.

(Meta AIが過学習しなければ)このとき出力されるAIは問題$P$を解くことができるのです.

実験設定

さて,AIを出力するといっても,不定形のものを学習することはできません.

そこで本記事では,出力されるAIはLinear Regressionの重みとします.

また,入力として与える問題$P$は,データ数および特徴量数が固定の回帰問題とします.

この実験設定でうまくいかないのであれば,どんなAIもつくることは困難でしょう.
problemToAI (2).jpg

問題の生成

さて,Meta AIを学習させるためには大量の問題が必要となります.現実的に(データ数と特徴量数が同じ)問題を大量に集めることは不可能なので,今回は問題を生成することにします.

いま,作られるAIは回帰問題を解くAIなので,ランダムに回帰問題を作ります.
このとき,問題の特徴量$X$は,ガウス分布から生成します.このとき平均値および標準偏差は[0.0,1.0]の任意の数にします.
(一様分布にしたらMeta AIの学習があんまりうまくいかなかった)

weight = np.random.random(features_num)
X = []
for _ in range(features_num):
    x = np.random.normal(loc=random.random(), scale=random.random(),size=instance_num)
    X.append(x)
X = np.array(X).T
#X = np.random.random((instance_num,features_num))
y = np.sum(X*weight, axis=1)

また,この問題をMeta AIに与えるときのMeta特徴量は,めんどうなので$X$をflatにして$y$とconcatした物にします.

def makeProblemFeatures(X,y):
    features = np.concatenate([X.flatten(),y])
    return features

前述のとおり,Meta AIはLinear Regressionの重みとします.
この問題を解いた時の重みは明らかにweightです.
これを好きな学習データ数分生成します.
今回は,暇だったので1000000問題つくりました.また,各問題のデータ数は100,特徴量数は3です.
過学習すると,正則化とか表現力とかパラメータ調整とか,面倒な話が出てくるので,問題数でMeta AIの過学習を抑えます.

import numpy as np
import pandas as pd
import pickle
import random
import sys

def makeProblemFeatures(X,y):
    features = np.concatenate([X.flatten(),y])
    return features

features_num = 3
instance_num = 100
train_num = 1000000
test_num = 10000

dataset = {}

problem_F = []
problem_target = []
inputs = []
target = []
for i in range(train_num):
    weight = np.random.random(features_num)
    X = []
    for _ in range(features_num):
        x = np.random.normal(loc=random.random(), scale=random.random(),size=instance_num)
        X.append(x)
    X = np.array(X).T
    y = np.sum(X*weight, axis=1)
    problem_F.append(makeProblemFeatures(X,y))
    problem_target.append(weight)
    inputs.append(X)
    target.append(y)
    sys.stdout.write("\r making training dataset ({} / {})".format(i+1,train_num))
print("end")
problem_F = np.array(problem_F)
problem_target = np.array(problem_target)
inputs = np.array(inputs)
target = np.array(target)

dataset["train"] = {}
dataset["train"]["problem_F"] = problem_F
dataset["train"]["problem_target"] = problem_target
dataset["train"]["inputs"] = inputs
dataset["train"]["target"] = target


problem_F = []
problem_target = []
inputs = []
target = []
for i in range(test_num):
    weight = np.random.random(features_num)
    X = []
    for _ in range(features_num):
        x = np.random.normal(loc=random.random(), scale=random.random(),size=instance_num)
        X.append(x)
    X = np.array(X).T
    y = np.sum(X*weight, axis=1)
    problem_F.append(makeProblemFeatures(X,y))
    problem_target.append(weight)
    inputs.append(X)
    target.append(y)
    sys.stdout.write("\r making test dataset ({} / {})".format(i+1,test_num))
print("end")

problem_F = np.array(problem_F)
problem_target = np.array(problem_target)
inputs = np.array(inputs)
target = np.array(target)

dataset["test"] = {}
dataset["test"]["problem_F"] = problem_F
dataset["test"]["problem_target"] = problem_target
dataset["test"]["inputs"] = inputs
dataset["test"]["target"] = target
f = open("dataset","wb")
pickle.dump(dataset,f)
f.close()

testデータも同様に生成しました.testデータは10000問題です.
また,各問題の$X,y$(input, target)を保存していますが以後使わないです.

Meta AIのモデル

AIをつくるAIのモデルですが,全結合のNeural Networkとします.
入力は問題のmeta 特徴量として,400次元($100 \times 3 + 100$)のVectorが入ります.
出力は3次元Vectorです.
中間層は8層にしました.10層full connection layerにロマンを感じたので.
ほんとは,$X$と$y$を別入力で入れて,何層か挟んでからconcatしたほうがいい気がします.
モデルは,pytorchで書きました.初めて書いたけど思ってたより書きやすいですね.

import torch
import torch.nn as nn
import torch.nn.functional as functional

from torch.autograd import Variable 
import torch.optim as optim 
import torch.utils.data 
from torchvision import datasets, models, transforms
import numpy as np
import sys
import pickle
from sklearn.preprocessing import StandardScaler 
from sklearn.preprocessing import MinMaxScaler

def multi_output_loss(out, weight):
    loss = torch.sum(torch.abs(out - weight))/(weight.shape[0]*weight.shape[1])
    return loss


class Network(nn.Module):
    def __init__(self, feature_num):
        super(Network, self).__init__()
        self.net = nn.Sequential(
            nn.Linear(feature_num, 1000),
            nn.ReLU(),
            nn.Linear(1000, 1000),
            nn.ReLU(),
            nn.Linear(1000, 1000),
            nn.ReLU(),
            nn.Linear(1000, 1000),
            nn.ReLU(),
            nn.Linear(1000, 1000),
            nn.ReLU(),
            nn.Linear(1000, 1000),
            nn.ReLU(),
            nn.Linear(1000, 1000),
            nn.ReLU(),
            nn.Linear(1000, 1000),
            nn.ReLU(),
            nn.Linear(1000, 100),
            nn.ReLU(),
            nn.Linear(100, 3)
        )

    def forward(self, x):
        x = self.net(x)
        return x

    def fit(self, problem_F, weight):
        device = torch.device("cuda")
        #device = torch.device("cpu")
        train = torch.utils.data.TensorDataset(
            torch.from_numpy(problem_F), torch.from_numpy(weight))
        train_loader = torch.utils.data.DataLoader(train, batch_size=1000, shuffle=True)

        optimizer = optim.Adam(self.parameters(), lr=0.001)
        model  = self.to(device)
        max_epoch = 100
        for epoch in range(max_epoch):
            training_loss = 0
            for i, data in enumerate(train_loader):
                _F, _weight = data
                _F = Variable(_F.to(device))
                _weight = Variable(_weight.to(device))

                optimizer.zero_grad()
                out = model(_F)
                loss = multi_output_loss(out, _weight)
                training_loss += loss.item()
                loss.backward()
                optimizer.step()
            training_loss =training_loss / len(train_loader)
            print("training ({} / {}), train Loss : {}".format(epoch+1,max_epoch, training_loss))
        print(" end")
    def predict(self, F):
        model  = self.to("cpu")
        F = Variable(torch.from_numpy(F))
        with torch.no_grad():
            w = model(F).detach().numpy()
        return w

if __name__ == "__main__":
    f = open("dataset","rb")
    dataset = pickle.load(f)
    f.close()

    problem_F = dataset["train"]["problem_F"]
    weight = dataset["train"]["problem_target"]
    problem_F = problem_F.astype(dtype=np.float32)
    weight = weight.astype(dtype=np.float32)
    print("train data num {}, train data feature num {}".format(problem_F.shape[0], problem_F.shape[1]))

    model = Network(problem_F.shape[1])
    model.fit(problem_F, weight)
    print("---------train--------")
    pred_w = model.predict(problem_F)
    for i in range(10):#len(pred_w)):
        print("true weight {}, pred weight {}".format(weight[i], pred_w[i]))

    train_error = np.sum(np.abs(pred_w - weight))/(weight.shape[0] * weight.shape[1])

    problem_F = dataset["test"]["problem_F"]
    weight = dataset["test"]["problem_target"]
    problem_F = problem_F.astype(dtype=np.float32)
    weight = weight.astype(dtype=np.float32)
    print("---------test--------")
    pred_w = model.predict(problem_F)
    for i in range(10):#len(pred_w)):
        print("true weight {}, pred weight {}".format(weight[i], pred_w[i]))

    test_error = np.sum(np.abs(pred_w - weight))/(weight.shape[0] * weight.shape[1])

    print("train error {}, test error {}".format(train_error, test_error))

    torch.save(model.state_dict(), "model")

Parameters

Meta AIの学習パラメータですが

  • Batch Size : 1000
  • optimizer : Adam
  • Learning rate : 0.001
  • max epoch : 100

にしました.
誤差関数ですが,平均絶対値誤差(MAE)を使いました.MSEでもそんなに変わらないです.

実験結果

結果からいうと不完全なAIの生成には成功しました.
testのMAE誤差が0.048程度なので,あんまり精度がよくないです.
ちょっと層数を増やしすぎたかもしれません.過学習はデータ数をもっと増やして,データ数でごり押せば何とかなりそう.

train error 0.03311155989583333, test error 0.047954313151041664

テストデータにおいて,生成されたLinear Regressionの重みは次通りです(10個だけ表示).
true weightが真のLinear Regressionの重み,
pred weightがMeta AIが予想したLinear Regressionの重みです.

---------test--------
true weight [0.2661951  0.00673665 0.6146182 ], pred weight [0.27162865 0.07822143 0.20651376]
true weight [0.6896427  0.00971592 0.55464643], pred weight [0.72070605 0.04845329 0.5430689 ]
true weight [0.17735223 0.1530034  0.21936643], pred weight [0.20701459 0.14589433 0.23499718]
true weight [0.9953587  0.30713415 0.09861181], pred weight [0.9829714  0.31441572 0.06271259]
true weight [0.1797012  0.34231284 0.12426861], pred weight [0.26220655 0.36576685 0.06143581]
true weight [0.09689087 0.9697109  0.697348  ], pred weight [0.18424456 0.95420986 0.32859597]
true weight [0.1987553  0.41723123 0.37762934], pred weight [0.16770707 0.482017   0.3129202 ]
true weight [0.7517576  0.05367153 0.34765583], pred weight [0.73589396 0.13084184 0.32682523]
true weight [0.40666658 0.30940092 0.5539815 ], pred weight [0.41471976 0.29739022 0.58724344]
true weight [0.6700682  0.20830701 0.5595491 ], pred weight [0.66049206 0.3199482  0.5781056 ]

なんとなく相対的に(3つの重みのスケール感)はあっている気がしますが,絶対値で見るとあんまりあっていません.
特に,少数点3桁になると予想しにくいようです.おそらく,値が小さくなると,その特徴量xのデータを使うことが予測のはずれを生むのでしょう.
精度向上は今後の課題ですね.

おわりに

本記事ではAIをつくるAIをやってみました.
もっとも簡単と想定されるLinear Regressionでも完全には作ることができなそうです.
また,本記事における実験は,すべての問題の特徴量数・データ数が同じであるという設定にしていますが,現実的にそんなことはありません.問題そのものの特徴量を抽出する方法を考えなければいけません.さらに,Linear Regressionで解ける問題のみを扱っていること,問題にノイズが含まれていないことも非現実的です.もっと言うと,実問題で学習する場合には,全ての問題を解く必要があります.今回のような100万個の問題を用意することは,不可能だと考えられます.
しかし,今回の実験によってAIをつくるAIが可能であることも示唆されています.もしも画像ベースなどの問題でも同じことが可能なのかを検証してみたいです.(とりあえず次は非線形問題かな)

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1