Created
May 5, 2023 00:00
-
-
Save srifqi/64ab787999e16a0cb18eb8117e737dd4 to your computer and use it in GitHub Desktop.
Contoh implementasi perceptron multilapis (MLP) dalam JavaScript ES6 murni tanpa pustaka pihak ketiga
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Program untuk membuat, melatih, dan menggunakan MLP sederhana | |
* | |
* Berikut contoh implementasi perceptron multilapis (MLP) dalam JavaScript ES6 | |
* murni tanpa pustaka pihak ketiga. | |
* | |
* @author srifqi | |
* @license MIT License | |
*/ | |
/** ======= FUNGSI-FUNGSI KOMPONEN UTAMA ======= **/ | |
/** | |
* @typedef MLP | |
* @type {object} | |
* @property {number[][]} lapisan Matriks bobot antarlapisan | |
* @property {number[][]} bias Matriks bobot bias tiap lapisan (setelah lapisan masukan) | |
* @property {function[]} aktivasi Daftar fungsi aktivasi tiap lapisan (setelah lapisan masukan) | |
* @property {function[]} derAktivasi Daftar turunan fungsi aktivasi tiap lapisan (setelah lapisan masukan) | |
*/ | |
/** | |
* Fungsi utama untuk menyusun MLP | |
* | |
* @param {number[]} ukuran Daftar ukuran tiap lapisan (termasuk lapisan masukan) | |
* @param {number[]} aktivasi Daftar aktivasi tiap lapisan (setelah lapisan masukan) | |
* @returns {MLP} Model berupa MLP dari parameter yang diberikan | |
*/ | |
function buatMLP(ukuran, aktivasi) { | |
const model = { | |
lapisan: [], | |
bias: [], | |
aktivasi: [], | |
derAktivasi: [] | |
}; | |
for (let i = 1; i < ukuran.length; i ++) { | |
const uA = ukuran[i - 1]; | |
const uB = ukuran[i]; | |
model.lapisan.push(buatMatriks2DAcak(uB, uA)); | |
model.bias.push(buatMatriks2DAcak(uB, 1)); | |
} | |
for (let i = 0; i < aktivasi.length; i ++) { | |
const ai = aktivasi[i]; | |
model.aktivasi.push(daftarGenAktivasi[ai]); | |
model.derAktivasi.push(daftarDerAktivasi[ai]); | |
} | |
return model; | |
} | |
/** | |
* Fungsi utama untuk melatih MLP | |
* | |
* Pelatihan dilakukan secara stokastik, yaitu pembaruan parameter dilakukan | |
* tiap entri dan tidak diambil rata-rata. | |
* | |
* @param {MLP} model Model yang akan dilatih | |
* @param {number[][]} X Daftar masukan dari set latihan | |
* @param {number[][]} Y Daftar keluaran (hasil/target) dari set latihan | |
* @param {Function} fungsiLajuBelajar Fungsi untuk mengatur tetapan laju belajar | |
* @param {number} maksIter Jumlah iterasi maksimum | |
* @returns {MLP} Model hasil pelatihan | |
*/ | |
function latih(model, X, Y, fungsiLajuBelajar, maksIter) { | |
if (X.length != Y.length) { | |
throw new Error(`Ukuran X dan Y tidak sama: ${X.length} dengan ${Y.length}.`); | |
} | |
let lajuBelajarSebelumnya = 0; | |
// tiap iterasi | |
for (let iter = 1; iter <= maksIter; iter ++) { | |
const lajuBelajar = fungsiLajuBelajar(iter - 1); | |
if (lajuBelajarSebelumnya != lajuBelajar) { | |
console.log(`Laju belajar diatur ke ${lajuBelajar}.`); | |
lajuBelajarSebelumnya = lajuBelajar; | |
} | |
let benar = 0; | |
let jumlahGalat = 0; | |
// tiap entri | |
for (let k = 0; k < X.length; k ++) { | |
// rambat maju | |
const masukan = transposisi([X[k]]); | |
const daftarNilai = rambatMaju(model, masukan); | |
const keluaran = daftarNilai[daftarNilai.length - 1]; | |
// untuk menghitung akurasi | |
if (maksArg(transposisi(keluaran)[0]) == maksArg(Y[k])) { | |
benar ++; | |
} | |
// rambat mundur | |
let galat = kurang(keluaran, transposisi([Y[k]])); | |
jumlahGalat += transposisi(galat)[0].reduce((a, v) => a + v); | |
// tanpa perkalian dengan turunan pada lapisan keluaran | |
for (let i = model.lapisan.length - 1; i >= 0; i --) { | |
const galatLapisan = kali( | |
galat, | |
transposisi(daftarNilai[i]), | |
lajuBelajar | |
); | |
const galatBiasLapisan = kali( | |
galat, | |
lajuBelajar | |
) | |
model.lapisan[i] = kurang(model.lapisan[i], galatLapisan); | |
model.bias[i] = kurang(model.bias[i], galatBiasLapisan); | |
if (i > 0) { | |
galat = kali(transposisi(model.lapisan[i]), model.derAktivasi[i - 1](galat)); | |
} | |
} | |
} | |
const akurasi = benar / X.length; | |
const rerataGalat = jumlahGalat / X.length; | |
console.log(`Iterasi ${iter}/${maksIter} | Galat: ${rerataGalat} | Akurasi: ${benar}/${X.length} / ${akurasi}`); | |
} | |
return model; | |
} | |
/** | |
* Fungsi utama untuk memprediksi dengan MLP yang diberikan | |
* | |
* @param {MLP} model Model yang akan melakukan prediksi | |
* @param {number[][]} X Daftar masukan | |
* @returns {Object.<string,*>} | |
*/ | |
function prediksi(model, X) { | |
const hasil = { | |
keluaran: [], | |
terpilih: [] | |
} | |
for (let k = 0; k < X.length; k ++) { | |
const masukan = transposisi([X[k]]); | |
const daftarNilai = rambatMaju(model, masukan); | |
const keluaran = daftarNilai[daftarNilai.length - 1]; | |
const terpilih = maksArg(transposisi(keluaran)[0]); | |
hasil.keluaran.push(keluaran); | |
hasil.terpilih.push(terpilih); | |
} | |
return hasil; | |
} | |
/** ======= FUNGSI-FUNGSI BANTUAN MATEMATIKA ======= **/ | |
/** | |
* Fungsi bantuan untuk membuat matriks 2 dimensi dengan isi tertentu (seragam) | |
* | |
* @param {number} baris Jumlah baris | |
* @param {number} kolom Jumlah kolom | |
* @param {number} [nilai=0] Nilai tiap elemen matriks | |
* @returns {number} Matriks baru sesuai parameter yang diberikan | |
*/ | |
function buatMatriks2D(baris, kolom, nilai) { | |
if (nilai == undefined) { | |
nilai = 0; | |
} | |
return Array.from(Array(baris), () => Array.from(Array(kolom), () => nilai)); | |
} | |
/** | |
* Fungsi bantuan untuk membuat matriks 2 dimensi dengan isi acak | |
* | |
* @param {number} baris Jumlah baris | |
* @param {number} kolom Jumlah kolom | |
* @returns {number} Matriks baru sesuai parameter yang diberikan | |
*/ | |
function buatMatriks2DAcak(baris, kolom) { | |
return Array.from(Array(baris), () => Array.from(Array(kolom), () => Math.random())); | |
} | |
/** | |
* Fungsi bantuan untuk melakukan transposisi matriks | |
* | |
* @param {number[][]} matriks Matriks yang akan ditransposisi | |
* @returns Hasil transposisi matriks | |
*/ | |
function transposisi(matriks) { | |
let hasil = buatMatriks2D(matriks[0].length, matriks.length); | |
for (let i = 0; i < matriks.length; i ++) { | |
for (let j = 0; j < matriks[0].length; j ++) { | |
hasil[j][i] = matriks[i][j]; | |
} | |
} | |
return hasil; | |
} | |
/** | |
* Fungsi bantuan untuk mengalikan beberapa nilai | |
* | |
* Nilai yang diberikan bisa berupa matriks ataupun skalar. | |
* | |
* @param {...number|number[][]} elemen Daftar yang akan dikalikan | |
* @returns {number|number[][]} Hasil perkalian | |
*/ | |
function kali(...elemen) { | |
if (elemen.length == undefined || elemen.length <= 1) { | |
throw new Error("Hanya ada satu yang akan dikalikan."); | |
} | |
let hasil = elemen[0]; | |
for (let q = 1; q < elemen.length; q ++) { | |
let ki = hasil; | |
let ka = elemen[q]; | |
// keduanya skalar | |
if (ki.length == undefined && ka.length == undefined) { | |
hasil = ki * ka; | |
// keduanya matriks | |
} else if (ki.length >= 1 && ka.length >= 1) { | |
if (ki[0].length != ka.length) { | |
let kiy = ki.length; | |
let kix = ki[0].length; | |
let kay = ka.length; | |
let kax = ka[0].length; | |
throw new Error(`Ukuran matriks tidak sesuai: ${kiy}x${kix} dengan ${kay}x${kax}.`); | |
} | |
hasil = buatMatriks2D(ki.length, ka[0].length); | |
for (let i = 0; i < ki.length; i ++) { | |
for (let j = 0; j < ka[0].length; j ++) { | |
let jumlah = 0; | |
for (let k = 0; k < ka.length; k ++) { | |
jumlah += ki[i][k] * ka[k][j]; | |
} | |
hasil[i][j] = jumlah; | |
} | |
} | |
// salah satunya matriks | |
} else { | |
let skalar = ki; | |
let matriks = ka; | |
if (ka.length == undefined) { // cukup periksa salah satunya | |
skalar = ka; | |
matriks = ki; | |
} | |
for (let i = 0; i < matriks.length; i ++) { | |
for (let j = 0; j < matriks[0].length; j ++) { | |
matriks[i][j] *= skalar; | |
} | |
} | |
hasil = matriks; | |
} | |
} | |
return hasil; | |
} | |
/** | |
* Fungsi bantuan untuk menambahkan dua matriks | |
* | |
* Kedua matriks harus berukuran sama. | |
* | |
* @param {number[][]} A Matriks A | |
* @param {number[][]} B Matriks B | |
* @returns {number[][]} Hasil pertambahan kedua matriks: A + B. | |
*/ | |
function tambah(A, B) { | |
if (A.length != B.length || A[0].length != B[0].length) { | |
let Ay = A.length; | |
let Ax = A[0].length; | |
let By = B.length; | |
let Bx = B[0].length; | |
throw new Error(`Ukuran matriks tidak sama: ${Ay}x${Ax} dengan ${By}x${Bx}.`); | |
} | |
for (let i = 0; i < A.length; i ++) { | |
for (let j = 0; j < A[0].length; j ++) { | |
A[i][j] += B[i][j]; | |
} | |
} | |
return A; | |
} | |
/** | |
* Fungsi bantuan untuk mengurangkan dua matriks | |
* | |
* Kedua matriks harus berukuran sama. | |
* | |
* @param {number[][]} A Matriks A | |
* @param {number[][]} B Matriks B | |
* @returns {number[][]} Hasil pengurangan matriks A oleh matriks B: A - B. | |
*/ | |
function kurang(A, B) { | |
if (A.length != B.length || A[0].length != B[0].length) { | |
let Ay = A.length; | |
let Ax = A[0].length; | |
let By = B.length; | |
let Bx = B[0].length; | |
throw new Error(`Ukuran matriks tidak sama: ${Ay}x${Ax} dengan ${By}x${Bx}.`); | |
} | |
for (let i = 0; i < A.length; i ++) { | |
for (let j = 0; j < A[0].length; j ++) { | |
A[i][j] -= B[i][j]; | |
} | |
} | |
return A; | |
} | |
/** | |
* Fungsi bantuan untuk mencari indeks yang memiliki nilai maksimum | |
* | |
* Jika ada banyak nilai maksimum yang sama, indeks paling kecil yang dipilih. | |
* | |
* @param {number[]} daftar Daftar yang akan diambil indeks dari maksimumnya | |
* @returns {number} Indeks yang memiliki nilai maksimum | |
*/ | |
function maksArg(daftar) { | |
if (daftar.length == undefined) { | |
throw new Error("Masukan tidak berupa daftar."); | |
} | |
if (daftar.length == 0) { | |
throw new Error("Daftar yang diberikan kosong."); | |
} | |
let indeksMaks = 0; | |
for (let q = 1; q < daftar.length; q ++) { | |
if (daftar[q] > daftar[indeksMaks]) { | |
indeksMaks = q; | |
} | |
} | |
return indeksMaks; | |
} | |
/** | |
* Fungsi bantuan untuk melakukan rambatan maju | |
* | |
* @param {MLP} model Model yang digunakan | |
* @param {number[][]} X Daftar masukan | |
* @returns {number[][]} Daftar keluaran tiap lapisan (setelah lapisan masukan) | |
*/ | |
function rambatMaju(model, X) { | |
let nilai = X; | |
let daftarHasil = [nilai]; | |
for (let i = 0; i < model.lapisan.length; i ++) { | |
nilai = kali(model.lapisan[i], nilai); | |
nilai = tambah(nilai, model.bias[i]); | |
nilai = model.aktivasi[i](nilai); | |
daftarHasil.push(nilai); | |
} | |
return daftarHasil; | |
} | |
/** | |
* Daftar fungsi aktivasi | |
* | |
* @type {Object<string,Function>} | |
*/ | |
let daftarGenAktivasi = {}; | |
/** | |
* Daftar turunan fungsi aktivasi | |
* | |
* @type {Object<string,Function>} | |
*/ | |
let daftarDerAktivasi = {}; | |
/** | |
* Fungsi bantuan untuk menerapkan ReLU terhadap tiap elemen matriks | |
* | |
* @param {number[][]} x Matriks masukan | |
* @returns {number[][]} Matriks hasil pemetaan ReLU | |
*/ | |
function genReLU(x) { | |
for (let i = 0; i < x.length; i ++) { | |
for (let j = 0; j < x[i].length; j ++) { | |
x[i][j] = Math.max(x[i][j], 0); | |
} | |
} | |
return x; | |
} | |
daftarGenAktivasi["relu"] = genReLU; | |
/** | |
* Fungsi bantuan untuk menerapkan turunan ReLU terhadap tiap elemen matriks | |
* | |
* @param {number[][]} x Matriks masukan | |
* @returns {number[][]} Matriks hasil pemetaan turunan ReLU | |
*/ | |
function derReLU(x) { | |
for (let i = 0; i < x.length; i ++) { | |
for (let j = 0; j < x[i].length; j ++) { | |
x[i][j] = x[i][j] >= 0 ? 1 : 0; | |
} | |
} | |
return x; | |
} | |
daftarDerAktivasi["relu"] = derReLU; | |
/** ======= PROGRAM UTAMA ======= **/ | |
/** | |
* Fungsi utama yang akan dijalankan pertama (lihat di bagian akhir) | |
*/ | |
function utama() { | |
const ukuran = [4, 5, 3]; | |
const aktivasi = ["relu", "relu"]; // hanya ada ReLU | |
const model = buatMLP(ukuran, aktivasi); | |
const lajuBelajarTetap = () => 0.001; | |
latih(model, X, Y, lajuBelajarTetap, 4); | |
console.log(prediksi(model, [X[0]])); //contoh prediksi | |
} | |
/** ======= EKSEKUSI PROGRAM ======= **/ | |
// Contoh untuk set data Iris (lihat di bawah) | |
const X = [ // daftar masukan (n, w, h) | |
[5.1, 3.5, 1.4, 0.2] | |
// ... | |
]; // daftar masukan (n, w, h) | |
const Y = [ // daftar label (n, j) | |
[1, 0, 0] | |
// ... | |
]; // daftar label (n, j) | |
utama(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const X = [ // daftar masukan (n, w, h) | |
[5.1,3.5,1.4,0.2], | |
[4.9,3.0,1.4,0.2], | |
[4.7,3.2,1.3,0.2], | |
[4.6,3.1,1.5,0.2], | |
[5.0,3.6,1.4,0.2], | |
[5.4,3.9,1.7,0.4], | |
[4.6,3.4,1.4,0.3], | |
[5.0,3.4,1.5,0.2], | |
[4.4,2.9,1.4,0.2], | |
[4.9,3.1,1.5,0.1], | |
[5.4,3.7,1.5,0.2], | |
[4.8,3.4,1.6,0.2], | |
[4.8,3.0,1.4,0.1], | |
[4.3,3.0,1.1,0.1], | |
[5.8,4.0,1.2,0.2], | |
[5.7,4.4,1.5,0.4], | |
[5.4,3.9,1.3,0.4], | |
[5.1,3.5,1.4,0.3], | |
[5.7,3.8,1.7,0.3], | |
[5.1,3.8,1.5,0.3], | |
[5.4,3.4,1.7,0.2], | |
[5.1,3.7,1.5,0.4], | |
[4.6,3.6,1.0,0.2], | |
[5.1,3.3,1.7,0.5], | |
[4.8,3.4,1.9,0.2], | |
[5.0,3.0,1.6,0.2], | |
[5.0,3.4,1.6,0.4], | |
[5.2,3.5,1.5,0.2], | |
[5.2,3.4,1.4,0.2], | |
[4.7,3.2,1.6,0.2], | |
[4.8,3.1,1.6,0.2], | |
[5.4,3.4,1.5,0.4], | |
[5.2,4.1,1.5,0.1], | |
[5.5,4.2,1.4,0.2], | |
[4.9,3.1,1.5,0.1], | |
[5.0,3.2,1.2,0.2], | |
[5.5,3.5,1.3,0.2], | |
[4.9,3.1,1.5,0.1], | |
[4.4,3.0,1.3,0.2], | |
[5.1,3.4,1.5,0.2], | |
[5.0,3.5,1.3,0.3], | |
[4.5,2.3,1.3,0.3], | |
[4.4,3.2,1.3,0.2], | |
[5.0,3.5,1.6,0.6], | |
[5.1,3.8,1.9,0.4], | |
[4.8,3.0,1.4,0.3], | |
[5.1,3.8,1.6,0.2], | |
[4.6,3.2,1.4,0.2], | |
[5.3,3.7,1.5,0.2], | |
[5.0,3.3,1.4,0.2], | |
[7.0,3.2,4.7,1.4], | |
[6.4,3.2,4.5,1.5], | |
[6.9,3.1,4.9,1.5], | |
[5.5,2.3,4.0,1.3], | |
[6.5,2.8,4.6,1.5], | |
[5.7,2.8,4.5,1.3], | |
[6.3,3.3,4.7,1.6], | |
[4.9,2.4,3.3,1.0], | |
[6.6,2.9,4.6,1.3], | |
[5.2,2.7,3.9,1.4], | |
[5.0,2.0,3.5,1.0], | |
[5.9,3.0,4.2,1.5], | |
[6.0,2.2,4.0,1.0], | |
[6.1,2.9,4.7,1.4], | |
[5.6,2.9,3.6,1.3], | |
[6.7,3.1,4.4,1.4], | |
[5.6,3.0,4.5,1.5], | |
[5.8,2.7,4.1,1.0], | |
[6.2,2.2,4.5,1.5], | |
[5.6,2.5,3.9,1.1], | |
[5.9,3.2,4.8,1.8], | |
[6.1,2.8,4.0,1.3], | |
[6.3,2.5,4.9,1.5], | |
[6.1,2.8,4.7,1.2], | |
[6.4,2.9,4.3,1.3], | |
[6.6,3.0,4.4,1.4], | |
[6.8,2.8,4.8,1.4], | |
[6.7,3.0,5.0,1.7], | |
[6.0,2.9,4.5,1.5], | |
[5.7,2.6,3.5,1.0], | |
[5.5,2.4,3.8,1.1], | |
[5.5,2.4,3.7,1.0], | |
[5.8,2.7,3.9,1.2], | |
[6.0,2.7,5.1,1.6], | |
[5.4,3.0,4.5,1.5], | |
[6.0,3.4,4.5,1.6], | |
[6.7,3.1,4.7,1.5], | |
[6.3,2.3,4.4,1.3], | |
[5.6,3.0,4.1,1.3], | |
[5.5,2.5,4.0,1.3], | |
[5.5,2.6,4.4,1.2], | |
[6.1,3.0,4.6,1.4], | |
[5.8,2.6,4.0,1.2], | |
[5.0,2.3,3.3,1.0], | |
[5.6,2.7,4.2,1.3], | |
[5.7,3.0,4.2,1.2], | |
[5.7,2.9,4.2,1.3], | |
[6.2,2.9,4.3,1.3], | |
[5.1,2.5,3.0,1.1], | |
[5.7,2.8,4.1,1.3], | |
[6.3,3.3,6.0,2.5], | |
[5.8,2.7,5.1,1.9], | |
[7.1,3.0,5.9,2.1], | |
[6.3,2.9,5.6,1.8], | |
[6.5,3.0,5.8,2.2], | |
[7.6,3.0,6.6,2.1], | |
[4.9,2.5,4.5,1.7], | |
[7.3,2.9,6.3,1.8], | |
[6.7,2.5,5.8,1.8], | |
[7.2,3.6,6.1,2.5], | |
[6.5,3.2,5.1,2.0], | |
[6.4,2.7,5.3,1.9], | |
[6.8,3.0,5.5,2.1], | |
[5.7,2.5,5.0,2.0], | |
[5.8,2.8,5.1,2.4], | |
[6.4,3.2,5.3,2.3], | |
[6.5,3.0,5.5,1.8], | |
[7.7,3.8,6.7,2.2], | |
[7.7,2.6,6.9,2.3], | |
[6.0,2.2,5.0,1.5], | |
[6.9,3.2,5.7,2.3], | |
[5.6,2.8,4.9,2.0], | |
[7.7,2.8,6.7,2.0], | |
[6.3,2.7,4.9,1.8], | |
[6.7,3.3,5.7,2.1], | |
[7.2,3.2,6.0,1.8], | |
[6.2,2.8,4.8,1.8], | |
[6.1,3.0,4.9,1.8], | |
[6.4,2.8,5.6,2.1], | |
[7.2,3.0,5.8,1.6], | |
[7.4,2.8,6.1,1.9], | |
[7.9,3.8,6.4,2.0], | |
[6.4,2.8,5.6,2.2], | |
[6.3,2.8,5.1,1.5], | |
[6.1,2.6,5.6,1.4], | |
[7.7,3.0,6.1,2.3], | |
[6.3,3.4,5.6,2.4], | |
[6.4,3.1,5.5,1.8], | |
[6.0,3.0,4.8,1.8], | |
[6.9,3.1,5.4,2.1], | |
[6.7,3.1,5.6,2.4], | |
[6.9,3.1,5.1,2.3], | |
[5.8,2.7,5.1,1.9], | |
[6.8,3.2,5.9,2.3], | |
[6.7,3.3,5.7,2.5], | |
[6.7,3.0,5.2,2.3], | |
[6.3,2.5,5.0,1.9], | |
[6.5,3.0,5.2,2.0], | |
[6.2,3.4,5.4,2.3], | |
[5.9,3.0,5.1,1.8] | |
]; // daftar masukan (n, w, h) | |
const Y = [ // daftar label (n, j) | |
[1, 0, 0], | |
[1, 0, 0], | |
[1, 0, 0], | |
[1, 0, 0], | |
[1, 0, 0], | |
[1, 0, 0], | |
[1, 0, 0], | |
[1, 0, 0], | |
[1, 0, 0], | |
[1, 0, 0], | |
[1, 0, 0], | |
[1, 0, 0], | |
[1, 0, 0], | |
[1, 0, 0], | |
[1, 0, 0], | |
[1, 0, 0], | |
[1, 0, 0], | |
[1, 0, 0], | |
[1, 0, 0], | |
[1, 0, 0], | |
[1, 0, 0], | |
[1, 0, 0], | |
[1, 0, 0], | |
[1, 0, 0], | |
[1, 0, 0], | |
[1, 0, 0], | |
[1, 0, 0], | |
[1, 0, 0], | |
[1, 0, 0], | |
[1, 0, 0], | |
[1, 0, 0], | |
[1, 0, 0], | |
[1, 0, 0], | |
[1, 0, 0], | |
[1, 0, 0], | |
[1, 0, 0], | |
[1, 0, 0], | |
[1, 0, 0], | |
[1, 0, 0], | |
[1, 0, 0], | |
[1, 0, 0], | |
[1, 0, 0], | |
[1, 0, 0], | |
[1, 0, 0], | |
[1, 0, 0], | |
[1, 0, 0], | |
[1, 0, 0], | |
[1, 0, 0], | |
[1, 0, 0], | |
[1, 0, 0], | |
[0, 1, 0], | |
[0, 1, 0], | |
[0, 1, 0], | |
[0, 1, 0], | |
[0, 1, 0], | |
[0, 1, 0], | |
[0, 1, 0], | |
[0, 1, 0], | |
[0, 1, 0], | |
[0, 1, 0], | |
[0, 1, 0], | |
[0, 1, 0], | |
[0, 1, 0], | |
[0, 1, 0], | |
[0, 1, 0], | |
[0, 1, 0], | |
[0, 1, 0], | |
[0, 1, 0], | |
[0, 1, 0], | |
[0, 1, 0], | |
[0, 1, 0], | |
[0, 1, 0], | |
[0, 1, 0], | |
[0, 1, 0], | |
[0, 1, 0], | |
[0, 1, 0], | |
[0, 1, 0], | |
[0, 1, 0], | |
[0, 1, 0], | |
[0, 1, 0], | |
[0, 1, 0], | |
[0, 1, 0], | |
[0, 1, 0], | |
[0, 1, 0], | |
[0, 1, 0], | |
[0, 1, 0], | |
[0, 1, 0], | |
[0, 1, 0], | |
[0, 1, 0], | |
[0, 1, 0], | |
[0, 1, 0], | |
[0, 1, 0], | |
[0, 1, 0], | |
[0, 1, 0], | |
[0, 1, 0], | |
[0, 1, 0], | |
[0, 1, 0], | |
[0, 1, 0], | |
[0, 1, 0], | |
[0, 1, 0], | |
[0, 0, 1], | |
[0, 0, 1], | |
[0, 0, 1], | |
[0, 0, 1], | |
[0, 0, 1], | |
[0, 0, 1], | |
[0, 0, 1], | |
[0, 0, 1], | |
[0, 0, 1], | |
[0, 0, 1], | |
[0, 0, 1], | |
[0, 0, 1], | |
[0, 0, 1], | |
[0, 0, 1], | |
[0, 0, 1], | |
[0, 0, 1], | |
[0, 0, 1], | |
[0, 0, 1], | |
[0, 0, 1], | |
[0, 0, 1], | |
[0, 0, 1], | |
[0, 0, 1], | |
[0, 0, 1], | |
[0, 0, 1], | |
[0, 0, 1], | |
[0, 0, 1], | |
[0, 0, 1], | |
[0, 0, 1], | |
[0, 0, 1], | |
[0, 0, 1], | |
[0, 0, 1], | |
[0, 0, 1], | |
[0, 0, 1], | |
[0, 0, 1], | |
[0, 0, 1], | |
[0, 0, 1], | |
[0, 0, 1], | |
[0, 0, 1], | |
[0, 0, 1], | |
[0, 0, 1], | |
[0, 0, 1], | |
[0, 0, 1], | |
[0, 0, 1], | |
[0, 0, 1], | |
[0, 0, 1], | |
[0, 0, 1], | |
[0, 0, 1], | |
[0, 0, 1], | |
[0, 0, 1], | |
[0, 0, 1] | |
]; // daftar label (n, j) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment