|
import numpy as np
|
|
import json
|
|
|
|
class LayerConfig:
|
|
def __init__(self, name, size, activation):
|
|
self.name = name
|
|
self.size = size
|
|
self.activation = activation
|
|
|
|
class SimpleMLModel:
|
|
def __init__(self, layer_configs, learning_rate=0.01, loss='mse'):
|
|
self.learning_rate = learning_rate
|
|
self.loss = loss
|
|
self.layer_configs = layer_configs
|
|
self.model = self._init_model()
|
|
|
|
def _init_model(self):
|
|
model = {}
|
|
sizes = [self.layer_configs[0].size]
|
|
|
|
for config in self.layer_configs[1:]:
|
|
sizes.append(config.size)
|
|
|
|
for i in range(len(sizes) - 1):
|
|
model[f'W{i}'] = np.random.randn(sizes[i], sizes[i+1]) * 0.01
|
|
model[f'b{i}'] = np.zeros((1, sizes[i+1]))
|
|
|
|
return model
|
|
|
|
def forward(self, X):
|
|
activations = [X]
|
|
for i, config in enumerate(self.layer_configs[1:]):
|
|
W = self.model[f'W{i}']
|
|
b = self.model[f'b{i}']
|
|
X = np.dot(X, W) + b
|
|
|
|
if config.activation == 'relu':
|
|
X = np.maximum(0, X)
|
|
elif config.activation == 'sigmoid':
|
|
X = 1 / (1 + np.exp(-X))
|
|
elif config.activation == 'tanh':
|
|
X = np.tanh(X)
|
|
|
|
activations.append(X)
|
|
return activations
|
|
|
|
def backward(self, activations, y_true):
|
|
grads = {}
|
|
dA = activations[-1] - y_true
|
|
|
|
for i in reversed(range(len(self.model) // 2)):
|
|
dZ = dA * (activations[i+1] > 0)
|
|
grads[f'dW{i}'] = np.dot(activations[i].T, dZ) / y_true.shape[0]
|
|
grads[f'db{i}'] = np.sum(dZ, axis=0, keepdims=True) / y_true.shape[0]
|
|
if i > 0:
|
|
dA = np.dot(dZ, self.model[f'W{i}'].T)
|
|
|
|
return grads
|
|
|
|
def update_params(self, grads):
|
|
for i in range(len(self.model) // 2):
|
|
self.model[f'W{i}'] -= self.learning_rate * grads[f'dW{i}']
|
|
self.model[f'b{i}'] -= self.learning_rate * grads[f'db{i}']
|
|
|
|
def train(self, X, y, epochs=100):
|
|
for epoch in range(epochs):
|
|
activations = self.forward(X)
|
|
grads = self.backward(activations, y)
|
|
self.update_params(grads)
|
|
|
|
def predict(self, X):
|
|
activations = self.forward(X)
|
|
return activations[-1]
|
|
|
|
def save_model(self, filepath):
|
|
np.savez(filepath, **self.model)
|
|
|
|
def load_model(self, filepath):
|
|
data = np.load(filepath)
|
|
self.model = {k: data[k] for k in data}
|
|
|
|
def save_config(self, filepath):
|
|
config_list = []
|
|
for config in self.layer_configs:
|
|
config_list.append({
|
|
"name": config.name,
|
|
"size": config.size,
|
|
"activation": config.activation
|
|
})
|
|
|
|
with open(filepath, 'w') as f:
|
|
json.dump(config_list, f, indent=4)
|
|
|
|
def load_config(self, filepath):
|
|
with open(filepath, 'r') as f:
|
|
config_list = json.load(f)
|
|
|
|
self.layer_configs = []
|
|
for config_data in config_list:
|
|
self.layer_configs.append(LayerConfig(**config_data))
|
|
|
|
self.model = self._init_model()
|
|
|
|
input_size = 2
|
|
output_size = 1
|
|
|
|
|
|
X = np.array([
|
|
[10, 10],
|
|
[5, 5],
|
|
[15, 15],
|
|
], dtype=np.float32)
|
|
|
|
y = np.array([
|
|
[20],
|
|
[10],
|
|
[30],
|
|
], dtype=np.float32)
|
|
|
|
|
|
layer_configs = [
|
|
LayerConfig("input", input_size, None),
|
|
LayerConfig("hidden1", 16, "sigmoid"),
|
|
|
|
LayerConfig("output", output_size, None)
|
|
]
|
|
|
|
|
|
model = SimpleMLModel(layer_configs, learning_rate=0.01, loss='mse')
|
|
model.train(X, y, epochs=1000)
|
|
|
|
|
|
model.save_model("trained_model.npz")
|
|
|
|
|
|
predictions = model.predict(X)
|
|
|
|
print(predictions) |