So I made a simple cnn (in Lua not luau), it works amazing with a 5x5 grid. But I’ve been having trouble trying to upscale it to 20x20, I already have correlating training data but I just can’t seem to get it right. Any help would be appreciated.
local helper = require "modules/helper"
local dkjson = require "modules/dkjson"
local conv_layer = require "layers/Conv"
local fully_connected = require "layers/FullyConnected"
local max_pooling = require "layers/MaxPooling"
-- Activation Function (ReLU)
local function relu(x)
return math.max(0, x)
end
-- Softmax Function
local function softmax(input)
local sum_exp = 0
for i = 1, #input do
sum_exp = sum_exp + math.exp(input[i])
end
local output = {}
for i = 1, #input do
output[i] = math.exp(input[i]) / sum_exp
end
return output
end
-- Loss Function (Cross-Entropy)
local function cross_entropy_loss(output, target)
local loss = 0
for i = 1, #output do
loss = loss - target[i] * math.log(output[i] + 1e-10) -- Add small constant to avoid log(0)
end
return loss
end
-- Backpropagation and Training (Basic Gradient Descent)
local function train(input, filter, bias_conv, weights_fc, bias_fc, target, learning_rate, pool_size, stride)
-- Forward pass
local conv_out = conv_layer(input, filter, bias_conv, stride)
local pool_out = max_pooling(conv_out, pool_size, stride)
-- Flatten pooling output for fully connected layer
local flattened_input = {}
for i = 1, #pool_out do
for j = 1, #pool_out[i] do
table.insert(flattened_input, pool_out[i][j])
end
end
local fc_out = fully_connected(flattened_input, weights_fc, bias_fc)
for i = 1, #fc_out do
fc_out[i] = relu(fc_out[i])
end
local output = softmax(fc_out)
local loss = cross_entropy_loss(output, target)
-- Backward pass (Gradient Descent Update)
local d_output = {}
for i = 1, #output do
d_output[i] = output[i] - target[i]
end
-- Update Fully Connected Layer weights and biases
for i = 1, #weights_fc do
for j = 1, #weights_fc[i] do
weights_fc[i][j] = weights_fc[i][j] - learning_rate * d_output[i] * (flattened_input[j] or 0)
end
bias_fc[i] = bias_fc[i] - learning_rate * d_output[i]
end
-- Update Convolutional Layer filter and bias (Simple version)
for i = 1, #filter do
for j = 1, #filter[i] do
filter[i][j] = filter[i][j] - learning_rate * d_output[1] * (input[i] and input[i][j] or 0)
end
end
bias_conv = bias_conv - learning_rate * d_output[1]
return loss
end
local function generateWeightsFC(rows, cols)
local weights_fc = {}
local value = 0.1
for i = 1, rows do
weights_fc[i] = {}
for j = 1, cols do
weights_fc[i][j] = value
value = value + 0.1
end
end
return weights_fc
end
-- Saving the model
local function save_model(filename, filter, bias_conv, weights_fc, bias_fc)
local file = io.open(filename, "w")
local data = {
filter = filter,
bias_conv = bias_conv,
weights_fc = weights_fc,
bias_fc = bias_fc
}
file:write(dkjson.encode(data))
file:close()
end
-- Loading the model
local function load_model(filename)
local file = io.open(filename, "r")
local data = dkjson.decode(file:read("*a"))
file:close()
return data.filter, data.bias_conv, data.weights_fc, data.bias_fc
end
-- Example of 5x5 grids where digits 0 to 9 are drawn
local datasets = require "dataset"
local targets = {}
for i = 1, 10 do
targets[i] = {}
for j = 1, 10 do
targets[i][j] = 0
end
targets[i][i] = 1
end
local filter = {
{ 1, 0 },
{ 0, -1 }
}
local weights_fc = generateWeightsFC(10, 5)
local bias_fc = { 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0 }
local bias_conv = 0.0
local learning_rate = 0.01
local pool_size = 2
local stride = 1
filter, bias_conv, weights_fc, bias_fc = load_model("model.json")
local function epoch_train(epoches)
-- Training loop
for epoch = 1, epoches do
local total_loss = 0
for i = 1, #datasets do
local input = datasets[i]
local target = targets[i]
local loss = train(input, filter, bias_conv, weights_fc, bias_fc, target, learning_rate, pool_size, stride)
total_loss = total_loss + loss
end
if epoch % 1000 == 0 then
print("Epoch:", epoch, "Loss:", total_loss / #datasets)
end
end
save_model("model.json", filter, bias_conv, weights_fc, bias_fc)
end
epoch_train(10000)
-- Testing the model on a new input
local test_input = datasets[5 + 1] -- Testing with digit "5"
local conv_out = conv_layer(test_input, filter, bias_conv, stride)
local pool_out = max_pooling(conv_out, pool_size, stride)
-- Flatten pooling output for fully connected layer
local flattened_input = {}
for i = 1, #pool_out do
for j = 1, #pool_out[i] do
table.insert(flattened_input, pool_out[i][j])
end
end
local fc_out = fully_connected(flattened_input, weights_fc, bias_fc)
for i = 1, #fc_out do
fc_out[i] = relu(fc_out[i])
end
local output = softmax(fc_out)
-- Output the predicted class
local max_val = -math.huge
local predicted_class = 1
for i = 1, #output do
print(i - 1, output[i])
if output[i] > max_val then
max_val = output[i]
predicted_class = i
end
end
print("Predicted class:", predicted_class - 1, max_val)