import numpy as np

# 乱数シードを指定
np.random.seed(seed=0)

X_train = np.array([0, 1, 2])
T_train = np.array([1, 2, 0])

X_test = np.array([1, 2, 0])
T_test = np.array([2, 0, 1])

# one-hot-vectorに変換
X_train = np.eye(3)[X_train.astype("int")]
X_test = np.eye(3)[X_test.astype("int")]
T_train = np.eye(3)[T_train.astype("int")]
T_test = np.eye(3)[T_test.astype("int")]


def softmax(x):
    x = x.T
    _x = x - np.max(x)
    _x = np.exp(_x) / np.sum(np.exp(_x), axis=0)
    return _x.T
  
  
def cross_entropy_error(t, y):
    delta = 1e-8
    error = -np.mean(t * np.log(y + delta)) 
    return error
  

class SoftmaxCrossEntropyLoss():
    def __init__(self):
        self.y = None
        self.t = None
        self.loss = None
        
    def __call__(self, t, y):
        self.y = softmax(y)
        self.t = t.copy()
        self.loss = cross_entropy_error(self.t, self.y)
        return self.loss
    
    def backward(self):
        batch_size = self.t.shape[0]
        dy = self.y - self.t 
        dy /= batch_size
        return dy
      
      
class FullyConnectedLayer():
    def __init__(self, input_shape, output_shape):
        self.w = np.random.randn(input_shape, output_shape) * 0.01
        self.b = np.zeros(output_shape, dtype=np.float)
        self.x = None
        self.dw = None
        self.db = None
        
    def __call__(self, x):
        self.x = x
        out = np.dot(self.x, self.w) + self.b
        return out
    
    def backward(self, dout):
        dx = np.dot(dout, self.w.T) 
        batch_size = dx.shape[0]
        self.dw = np.dot(self.x.T, dout) 
        self.db = np.sum(dout, axis=0)
        return dx
      
      
class ReLU():
    def __init__(self):
        self.mask = None

    def __call__(self, x):
        self.mask = (x <= 0)
        out = x.copy()
        out[self.mask] = 0
        return out

    def backward(self, dout):
        dout[self.mask] = 0
        dx = dout

        return dx
      
      

# 分類モデル
class MLP_classifier():

    def __init__(self):
        '''
        構造
        x -> fc(783, 256) -> relu -> fc(256, 256) -> relu -> fc(256, 10) -> out
        '''
        
        # 層の定義
        self.fc1 = FullyConnectedLayer(3, 20)
        self.relu1 = ReLU()
        self.fc2 = FullyConnectedLayer(20, 20)
        self.relu2 = ReLU()
        self.fc3 = FullyConnectedLayer(20, 3)
        self.out = None
        
        # 損失関数の定義
        self.criterion = SoftmaxCrossEntropyLoss()

    def forward(self, x):
        '''
        順伝播
        '''
        
        x = self.relu1(self.fc1(x))
        x = self.relu2(self.fc2(x))
        self.out = self.fc3(x)
        
        # 勾配計算の都合上softmaxはこの順伝播関数内では行わない
        # 予測するときはさらにsoftmaxを通す必要がある
        return self.out

    def backward(self, t):
        '''
        逆伝播
        '''
        
        # 誤差を計算
        loss = self.criterion(t, self.out)
        # 勾配を逆伝播
        d = self.criterion.backward()
        d = self.fc3.backward(d)
        d = self.relu2.backward(d)
        d = self.fc2.backward(d)
        d = self.relu1.backward(d)
        d = self.fc1.backward(d)
        
        return loss

    def optimize_GradientDecent(self, lr):
        '''
        勾配降下法による全層のパラメータの更新
        '''
        for fc in [self.fc1, self.fc2, self.fc3]:
            fc.w -= lr * fc.dw
            fc.b -= lr * fc.db
              

# モデルの宣言
model = MLP_classifier()

# 学習率
lr = 0.005
# 学習エポック数
n_epoch = 2000

# n_epoch繰り返す
for n in range(n_epoch):
    # 訓練
    # Day1範囲外のため、ミニバッチは使用しない
    y = model.forward(X_train)
    loss = model.backward(T_train)
    model.optimize_GradientDecent(lr)
    
    # テスト
    y = model.forward(X_test)
    test_loss = model.backward(T_test)
    pred = softmax(y)
    accuracy = np.mean(np.equal(np.argmax(y, axis=1), np.argmax(T_test, axis=1)))
    #print(f'EPOCH {n + 1} | TRAIN LOSS {loss:.5f} | TEST LOSS {test_loss:.5f} | ACCURACY {accuracy:.2%}')
    #print("EPOCH", n + 1, "|", "TRAIN LOSS", loss, "|", "TEST LOSS", test_loss, "|", "ACCURACY", accuracy)
    classification_accuracy = accuracy

str = input()
y1 = [1, 0][str=="Sunny"]
y1 = [y1, 2][str=="Rainy"]

y1 = np.array([y1])
y1 = model.forward(np.eye(3)[y1.astype("int")])
print(["Sunny", "Cloudy", "Rainy"][np.argmax(y1, axis=1)[0]])
  
  
  

Measured memory_bytes: 12460