Pernahkah kalian menemui sebuah pernyataan logika yang tampak rumit dan sulit dipahami?
Misalnya, kita ingin mencari inversi pernyataan logika sederhana ini:
usia ≥ 18 atau punya izin khusus
Inversinya berikut ini:
tidak (usia ≥ 18 atau punya izin khusus)
Hmm, tidak memenuhi A atau B? Cukup rumit, ya. Bagaimana menyederhanakannya?
Dalam aljabar logika, terdapat sifat/identitas teorema De Morgan. Aku pertama kali tahu tentang teorema ini saat mengambil mata kuliah Sistem Digital (semester 1).
Teorema De Morgan menyatakan berikut:
¬(A ∧ B) = ¬A ∨ ¬B
¬(A ∨ B) = ¬A ∧ ¬B
Jadi, kita bisa menyederhanakan penyataan tadi sebagai berikut:
tidak (usia ≥ 18 atau punya izin khusus)
→ usia < 18 dan tidak punya izin khusus
Nah, setelah menggunakan sifat tadi, pernyataannya lebih mudah dipahami.
Kalau disebut pos tahun baru, ini sudah terlambat 19 hari. Hanya saja, aku ingin menulis sesuatu bulan ini. Sepertinya, cerita tentang diri sendiri juga baik. Aku juga masih buntu dalam memikirkan topik tulisan tentang kode atau pemrograman.
Sudah satu semester berlalu dalam perjalanan studi magisterku di UI, Depok. Saat ini masih dalam masa liburan semester. Semester dua sebentar lagi akan dimulai. Banyak hal baru yang kualami selama berada di perantauan (kesannya jauh sekali, tetapi memang cukup jauh). Aku bertemu banyak teman baru, suasana baru, budaya baru, bahasa baru, dan teknologi baru selama berada di kawasan yang sekarang punya singkatan Jabodetabekpunjur.
Walau mungkin juga sudah mengalaminya waktu kuliah sarjana, pertemanan kali ini berbeda karena akulah yang merantau (dari jauh). Sebelumnya, aku jadi "tuan rumah" (dekat sekali jarak rumah ke kampus), sedangkan teman-temankulah yang dari jauh. Aku juga bertemu orang-orang dari berbagai usia: ada yang baru saja mendapat gelar sarjana, ada yang sudah bekerja, ada yang sudah punya anak, dan ada yang sudah berambut putih. Tiap-tiap dari mereka punya kisah hidup yang unik.
Tempat di sekitar perguruan tinggi "populer" merupakan pencampuran budaya dari berbagai daerah di Indonesia. Aku menemukan banyak pedagang dan pengusaha yang merantau ke sana untuk berdagang atau membuka usaha, seperti rumah makan dan toko kelontong. Beberapa di antaranya sudah berhasil dan sudah berdagang atau menjalankan usahanya selama bertahun-tahun.
Untuk hal makanan, aku belum terpikir untuk mencoba banyak jajanan yang dijual di sana (lebih karena memang malas pergi, memang mager parah), padahal wilayah di sekitar instansi pendidikan terkenal dengan banyak penjual makanan/jajanan. Sebagian makan besarku di warteg: pilihan makanannya aman buatku. Meski begitu, aku juga mencoba banyak rumah makan yang ada di sekitar kampus.
Mengembalikan alat makan ke tempat khusus di kantin memang sederhana, tetapi tidak semua kampus menerapkan budaya ini. Itu salah satu budaya baru yang kualami di UI. Untuk kasus spesifik tersebut, baru kantin FK yang menerapkannya di UNS setahuku. Di sisi akademik, setidaknya yang kutahu, mahasiswa bisa ikut masuk kursus mata kuliah di EMAS (atau Scele untuk Fasilkom) tanpa mengambil mata kuliah tersebut secara resmi. Jadi, mahasiswa bisa ikut belajar tanpa berdampak pada nilai. Namun, aku belum tahu apakah hal itu kebijakan kampus, fakultas, atau dosen.
"Lu-gue end!" mungkin jadi kalimat yang cukup terkenal di sinetron. Namun, kata ganti lu dan gue memang lazim digunakan remaja di sana. Aku yang bukan asli orang sana lebih memilih saya-Pak/Bu-Mas/Mbak/Kak. Kata-kata lu/gue kayaknya bakal wagu kalau kuucapkan. Selain itu, ada teman kos yang orang Sunda. Jadi, aku cukup terpapar dengan suara bahasa Sunda.
Momen terbesar bagiku adalah bahwa ini pertama kali aku tinggal mandiri jauh dari rumah (tidak bisa tiap saat pulang ke rumah) dan pertama kali pula mengekos. Alhamdulillah, kos yang kusewa nyaman untuk ditinggali. Seperti kisah orang-orang yang merantau pertama kali, aku juga kadang terpikir bahwa sulit untuk tiba-tiba pulang kalau terjadi apa-apa. Jadi, aku perlu survei hal-hal di sekitar untuk persiapan kalau-kalau ada masalah yang terjadi.
Sepertinya itu saja yang terpikir untuk kutulis saat ini. Ide untuk mengepos ini tiba-tiba muncul dan, karena sempat, langsung kutuliskan saja di sini daripada terlupakan. Sampai jumpa!
Untuk versi bahasa Indonesia dari entri ini, klik di sini.
Hello!
A few days ago, I watched videos about how a simple MLP work. Then, related videos came up. (Ah, the YouTube reccomendation system is something, eh.) One of those is how to make a simple artificial neural network (ANN) with Python, but without third-party libraries, e.g. Tensorflow or PyTorch.
The video starts with explanations about its calculation or its mathematics. It continues with arranging the program code by creating small functions to help writing the code and also to tidy up the code's structure as a whole. What we have is a long, single script that does MLP.
What is MLP?
Multilayer perceptron is one form/structure of ANN which consists of some layers of perceptrons. Usually, this network consists of three main types of layers: (1) input layer, (2) hidden layers, and (3) output layer. All layers in an MLP have the same structure, i.e. consists of some perceptrons.
A diagram of an MLP network with three main layers
Perceptron is basically a simple classification component. It recieves inputs that is weighted by certain values plus its own bias and returns a value which can be assumed as activity level of the perceptron. Mathematically, a perceptron can be written like this below:
is the output value.
is the input value.
is the weight of each input.
is the bias value.
is an activation function.
A diagram of a single perceptron
Okay, how to do it?
Note: This explanation is just a summary. Read the original code below for the full explanation.
We starts with the model's data structure. What are needed in an MLP? To make it simple, we need four things below:
const model = {
layers: [], // interlayer weight matrix
bias: [], // bias weight matrix for each layer (after input layer)
activation: [], // list of activation function of each layer (after input layer)
derActivation: [] // list of activation function's (first) derivation of each layer (after input layer)
};
The next step is to create a function to feed-forward a model. We do a repeated matrix multiplication for each layer with some additional steps. The summary is the same with the perceptron equation above.
function feedForward(model, X) {
let values = X;
let listOfResults = [values];
for (let i = 0; i < model.layers.length; i ++) {
values = multiply(model.layers[i], values);
values = addition(values, model.bias[i]);
values = model.activations[i](values);
listOfResults.push(values);
}
return listOfResults;
}
After that, we can start the model's training. For each iteration, to make it easy, each sample/entry from the dataset is fed-forward, then calculate the error, and back-propagate the error; all in a single step.
This indeed is not an effective method to ensure that the training is stable because this method is very stochastic. The usual step for each iteration is doing feed-forward, error calculation, and back-propagation as a whole (or in batches) to make the training stable.
Note that there isn't any step to calculate the derivation of the output (last) layer's activation function. This is because, in my experience, the training of the model fails and its error value for other layers becomes NaN or others when the last layer's error is multiplied by the derivation of the output (last) layer's activation function.
for (let k = 0; k < X.length; k ++) {
// feed-forward
const inputs = transpose([X[k]]);
const listOfValues = feedForward(model, inputs);
const outputs = listOfValues[listOfValues.length - 1];
// back-propagation
let error = substract(outputs, transpose([Y[k]]));
totalError += transpose(error)[0].reduce((a, v) => a + v);
// no multiplication with output layer's activation function's derivation
for (let i = model.layers.length - 1; i >= 0; i --) {
const layerError = multiply(
error,
transpose(listOfValues[i]),
learningRate
);
const layerBiasError = multiply(
error,
learningRate
)
model.layers[i] = substract(model.layers[i], layerError);
model.bias[i] = substract(model.bias[i], layerBiasError);
if (i > 0) {
error = multiply(transpose(model.layers[i]), model.derActivations[i - 1](error));
}
}
}
After the model is trained, we can try to do prediction as below. This function doesn't just return the choice of the model, but also the raw outputs. We assume that the model is trained for a classification problem.
function predict(model, X) {
const result = {
outputs: [],
selected: []
}
for (let k = 0; k < X.length; k ++) {
const inputs = transpose([X[k]]);
const listOfValues = feedForward(model, inputs);
const outputs = listOfValues[listOfValues.length - 1];
const selected = argMax(transpose(outputs)[0]);
result.outputs.push(outputs);
result.selected.push(selected);
}
return result;
}
... and that's it generally, I think. There are a lot of details that need to be made, but those above are the general view of it. I used the full Iris dataset as training data and it got to 94% accuracy which is great in my opinion.