git

Theory…

Duluu (sekali) saya pernah menulis sedikiit (sekali) tentang cara pakai mercurial. Karena sekarang saya lebih sering pakai git di tempat kerja saya, maka saya menulis post ini dengan harapan agar tidak cepat lupa dengan perintah-perintah yang biasa dipakai di git dan kalaupun lupa cukup baca satu halaman ini saja biar cepat ingatnya. Tapi sebelum itu mungkin saya kasih sedikit cerita mengenai apa sih sebenarnya git itu?

Sebenarnya tujuan version control system (VCS) software seperti git dan kawan-kawannya itu intinya ada 2. Pertama untuk mengatasi masalah versioning. Dulu pasti pernah kan bikin laporan, terus diberi nama final di nama file-nya. Eh tapi tahunya dari dosennya disuruh ada revisi, yang ternyata tidak ada habis-habisnya, sehingga kita terpaksa membuat banyak file yang dinamai dengan “final rev1”, “final rev2”, “final final” dan seterusnya. Nah dengan VCS ini seharusnya kita cukup punya satu file saja, tapi seluruh sejarah revisi yang dilakukan tetap tersimpan sehingga kita bisa balik ke revisi yang sebelumnya kapan saja (Fitur ini juga ada sih di Microsoft Word, tapi kayaknya enggak banyak yang pakai).

meme
Kalau file dokumen atau kode tidak pakai version control system jadinya bisa seperti ini.

Problem kedua yang berusahan dipecahkan adalah tentang collaborative work. Misalkan kita dan teman kita mau menulis di file yang sama (tapi beda posisinya di dalam file, satu di bagian awal, dan satunya lagi di bagian akhir). Dengan VCS, hasil kerja kita akan digabung secara otomatis dengan kerjaan teman kita. Daripada saling kirim-kiriman email terus lupa file yang mana yang versi terbaru yang akibatnya kerjaan kita bisa hilang atau tertimpa dengan kerjaan teman kita. Salah satu kelemahan dari VCS mungkin adalah dia hanya men-support file dengan format text saja, tidak bisa binary seperti gambar atau video.

Zaman dahulu kala, VCS yang berkuasa adalah subversion. Ini saya pernah pakai waktu kerja praktik. Bedanya dengan git dan mercurial adalah sifatnya yang centralized. Artinya repository-nya sebenarnya hanya ada di satu server, dan setiap kita mau kerja, kita ambil semua file versi terbaru dari server. Ketika kita melakukan edit pada file dan ingin komit, maka kita juga harus mengirim commit-annya ke server tersebut. Akibatnya apabila server-nya mati, atau kita tidak ada akses ke sana (no internet), maka kita tidak bisa kerja sama sekali. Selain itu, menariknya adalah kalau kita mau commit, maka harus cepet-cepetan sama teman kita, karena kalau keduluan maka kita harus fetch, merge, terus commit lagi.

(Sedikit nostalgia. Dulu repository subversion tempat saya menaruh tugas kuliah itu di-host di Google Code yang sekarang sayangnya sudah tutup. Google memang suka resek, punya kebiasaan menutup service secara mendadak (halo Reader, Wave), . Jadi Jangan heran kalau 5 tahun lagi Google+ juga bisa saja ditutup.)

Git dan Mercurial sendiri tergolong ke distributed VCS. Ini artinya setiap kopi repository git merupakan exact copy dari yang ada di server. Kita bisa commit, checkout ke versi sebelumnya, dll, cukup dari repository lokal kita. Kelemahannya adalah di awal proses checkout (atau clone dalam istilah git) itu bisa lama sekali karena semua sejarah commit dari awal sampai akhir bakal dikopi. Untuk repository yang sudah punya sejarah panjang, ukurannya bisa mencapai Gigabyte!

Kalau git sama mercurial (hg) sendiri apa bedanya? Hmm, in general, keduanya memiliki kapabilitas yang sama.  Tinggal tergantung selera kita masing-masing, sama seperti banyak hal lain di komputer (vim vs emacs, tabs vs spasi, dll). Kalau dulu waktu kuliah saya sebenarnya sih lebih suka mercurial, karena repo-nya bisa diakses menggunakan http dari jaringan internet kampus (host di bitbucket) sedangkan git hanya bisa lewat ssh (host di github). Tapi sekarang git sudah bisa http, dan bitbucket sudah bisa host git. Jadi rasanya tidak ada perbedaan yang terlalu berarti.

…and Practice

Oke, kita langsung masuk saja ke cara menggunakan git, untuk basic workflow yang sederhana. Akan ada sedikit penjelasan untuk tiap command-nya biar enggak kayak komik di bawah ini. Sebenarnya untuk bisa berfungsi normal dalam kehidupan sehari-hari cuma sedikit kok command git yang harus dihafal dan dimengerti. Untuk kasus-kasus unik dan menantang bisa diselesaikan dengan bertanya di stackoverflow atau senior programmer di kantor masing-masing.

git
Classic xkcd. Tapi sarannya valid lho, ultimate solution-nya ya memang clone ulang dari awal.

> Pertama, ya instalasi. Kalau di ubuntu gampang tinggal jalanin ini di terminal:

sudo apt-get install git

Kalau Windows dan lainnya bisa lewat installer dari situs resminya https://git-scm.com/downloads.

> Kemudian, setup nama dan email. Cukup sekali di awal.

git config --global user.name "Muhammad Iqbal Tawakal"

git config --global user.email mit.iqi@gmail.com

Tentunya sesuaikan dengan nama dan email masing-masing ya.

> Setelah itu tinggal buat repository baru dari dalam sebuah folder

git init

Atau ambil dari repo yang sudah ada

git clone <repo_address> <target_directory>

> Commit time

Bisa dibilang ada beberapa tingkatan level file di dalam git. Pertama untracked files. Ini adalah file yang tidak di-track oleh git, namun ada di dalam direktori repository. Sebaiknya, semua file yang penting itu di-commit dan sisanya yang tidak penting (temporary files, output log) itu di-list di dalam file .gitignore. Gunakan git add <filename> untuk memasukkan untracked file ke staging area. Apabila file-nya sudah di-track, tapi isinya sudah diubah dari yang terakhir di-commit, itu termasuk ke uncommitted changes. Ini juga bisa dimasukkan ke staging area dengan perintah yang sama.

Semua file yang ada di staging area ini akan di commit ke dalam repository kalau kita menggunakan perintah git commit. Tambahkan parameter -m kalau kita ingin memberikan pesan komit dalam satu baris. Kalau tanpa -m maka git akan membuka text editor di terminal (seperti vim), terus kita harus memasukkan pesan kita di dalamnya. Save file untuk melanjutkan commit. Ilustrasinya bisa dilihat di bawah.

git stages 1
The many (faces) stages of git.

Nah gimana kalau kita berubah pikiran dan mau pindah dari stage atas ke bawah? Sebenarnya git akan memberikan petunjuk kalau kita ketik git status, tapi untuk gampangnya bisa dilihat di gambar berikut. Intinya reset dari staging ke uncommited, dan checkout dari uncommitted changes ke versi commit-annya yang terakhir.

git stages 2
Kebalikannya.

Kenapa modelnya seperti ini? Harus di-add setiap mau commit? Jadi sebuah commit itu sebaiknya adalah 1 self-contained logical changes. Jadi kalau misalkan banyak file yang berubah sejak commit terakhir, kita supervisi sendiri mana file yang akan di-commit dan mana yang tidak, atau nanti dimasukkan di commit yang berbeda. Ini menjaga agar kita tidak (secara tidak sengaja) memasukkan file yang tidak ada hubungannya dengan apa yang sebenarnya mau kita commit. (Di mercurial, setiap commit akan memasukkan semua file yang berubah dari sejak terakhir commit).

Selain itu sebaiknya tiap commit itu tidak mengandung error. Kalau hal terakhir ini sebenarnya bisa di-enforce dengan menggunakan static code analysis tools seperti linter atau dengan code-review dan automatic testing. Mungkin bisa dibahas di post-post berikutnya.

Sedikit tambahan lagi. Untuk melihat apa yang berubah antara files di uncommitted changes dengan versi originalnya di committed revision bisa dengan perintah git diff. Untuk melihat status dari setiap file (sedang berada di stage mana) bisa menggunakan git status. Untuk melihat daftar commit apa saja yang sudah diperbuat, gunakan perintah git log.

And that’s it! That’s all you ever need to know if you want to use git all by yourself on a single machine for versioning. Of course, git offers more features than just for this kind of usage. Keep reading if you’d like to indulge yourself more in the world of git.

> Branching

Branching, atau percabangan, adalah salah satu fitur andalan git agar proses development kita bisa lebih bersih lagi dari error. Gambar di bawah menunjukkan skema branching yang lazim dipakai untuk suatu proses development software. Ada satu branch utama, bernama master, yang berisi kode yang siap release ke production. Active development dilakukan di branch develop yang secara berkala akan di merge ke branch master ketika sudah siap.

branch
Typical development branch. Gambar dikopi dari slide tech talk Salestock.

Setiap kita hendak mengimplemen fitur baru, maka kita buat branch baru dengan nama fitur tersebut yang baru akan kita merge ketika implementasinya sudah selesai. Hal ini akan membuat proses development menjadi lebih rapi, karena semua logical changes dan commit yang memang khusus untuk fitur itu saja terisolasi di dalam satu branch, dan tidak mengganggu developer lain yang hendak mengimplementasi fitur lain.

Perintah git branch akan memunculkan semua nama branch yang ada di repo kita. Kalau kita mau buat branch baru, gunakan perintah git checkout <hash_commit/another_branch_name> -b <new_branch_name> atau hilangkan commit hash kalau kita mau bikin branch baru dari HEAD (HEAD ini menunjuk ke commit atau branch yang sedang aktif di workspace). Kalau mau berpindah-pindah branch, cukup ketik git checkout <branch_name>. Branch yang lama dapat dihapus dengan cara git branch -d <branch_name>.

> Collaborative Work

Sekrang kita akan membahas satu fitur terpenting lainnya dari VCS, yaitu untuk bekerja secara kolaborasi. Pejamkan mata Anda dan bayangkan skenario seperti berikut. Ada satu repository yang terletak di server origin, yang dulu dikerjakan oleh Misty, dan 2 orang developer baru, Ash dan Brock, yang akan melanjutkan pekerjaan yang dahulu sempat terhenti.

git awal
Phase 1: Fresh from the server.

Pertama, Ash dan Brock meng-clone repo tersebut ke laptop mereka masing-masing sehingga sekarang mereka mempunyai complete copy dari repository tersebut. Ash kemudian menambahkan fitur baru dan meng-commit-nya. Agar kerjaannya itu bisa dipakai orang lain, maka Ash melakukan push perubahannya kembali ke origin server dengan melakukan git push origin master.

git tengah
Phase 2: After push

Brock juga tak mau kalah dengan Ash dan juga menambah fitur baru yang berbeda. Namun ketika dia hendak mem-push commit-nya, server origin menolak karena Ash sudah lebih dulu melakukannya. Yang Brock harus lakukan adalah mengambil commit-nya si Ash dan menggabungkannya dengan commit-nya.

git tengah 2
Phase 3: Merging

Perintah yang digunakan untuk mengambil commit baru dari server adalah git fetch. Setelah fetch ada 2 opsi yang bisa dipilih, merge atau rebase. git merge akan menggabungkan 2 commit yang berbeda menjadi dengan membuat commit baru. Sedangkan git rebase akan meng-apply commit kita ke atas commit yang satunya. Sebenarnya ada shortcut yang bisa dipakai, untuk fetch+merge, bisa menggunakan git pull. Sedangkan untuk fetch+rebase bisa menggunakan git pull --rebase.

git akhir
Final phase: Teamwork is beautiful, isn’t it?

Setelah itu Brock bisa mencoba melakukan push lagi dan kali berhasil sehingga  Akhirnya server origin menjadi berisi commit dari 2 orang tersebut, dan siap untuk di-clone orang berikutnya yang akan melanjutkan pekerjaan mereka berdua.

> Tips and Trick

Untuk mempersingkat penulisan beberapa command yang sering dipakai, biar lebih menghemat keystroke dan juga umur, gunakanlah perintah alias. Hasilnya juga jadi lebih mirip command di hg. Misalkan untuk 2 perintah yang paling sering digunakan.

git config --global alias.ci commit

git config --global alias.st status

Kalau yang ini rekomendasi saya untuk dipakai melihat commit tree dalam bentuk grafik di dalam terminalnya langsung. Akan sangat memudahkan untuk mendapatkan mental image dari kondisi repositori sekarang. Apalagi kalau branch-nya sudah banyak dan rumit.

git config --global alias.tree "log --graph --decorate --oneline --all"

Untuk melihat semua shortcut (plus informasi lainnya) bisa dengan:

git config --list

Untuk dokumentasi yang lebih lengkap, dan berisi semua hal yang bisa diketahui dari git, bisa lewat buku progit yang kebetulan full version-nya dibuka di sini: https://git-scm.com/book/en/v2

Kalau butuh visualisasi lebih mendetail untuk operasi-operasi git lainnya, bisa ditilik di tautan berikut: http://marklodato.github.io/visual-git-guide/index-en.html

Wah, jadinya panjang juga. Demikianlah, semoga berguna.

Salam.

Iklan

Whatsapp Chat Analyzer

Jadi ceritanya mahasiswa asal Indonesia yang tinggal di Stockholm menggunakan whatsapp sebagai media komunikasi untuk saling berbagi informasi hingga keluh kesah. Karena jumlah pesertanya yang memang tidak terlalu banyak (20+++), saya rasa, dibanding grup Facebook atau mailing list, group chat whatsapp memang lebih cocok untuk komunikasi yang membutuhkan respons yang relatif cepat.

Berawal dari rasa ingin tahu akan esensi dari pembicaraan di grup whatsapp tersebut, saya pun penasaran dan ingin menganalisis kontennya. Bak gayung bersambut, ternyata whatsapp menyediakan fitur untuk mengarsip pembicaraan dan mengirimkannya lewat email. Caranya cukup tahan percakapan yang ingin diarsip. Kemudian pilih ‘Email conversation’ > ‘Without media’. Gambar di bawah menunjukkan langkah-langkah tersebut. Nama dan tulisan disamarkan untuk menjaga privasi.

archive conversationwithout media

Hasilnya adalah sebuah file text. Setiap pesan chat ditulis pada satu baris dengan format “Tanggal – User: Pesan” (tanpa tanda kutip). Kode python dibawah digunakan untuk membaca file tersebut.

def read(filename):
    """ Read text file containing whatsapp chat and return the list of list of time, author, and its text
    :param filename: the filename of the chat text file
    :return: chat 2d list
    """
    chat = []
    with open(filename, 'r') as f:
        for line in f:
            lines = line.split(' - ')  # Divide between date and the rest
            if len(lines) > 1:
                lines2 = lines[1].split(': ')  # Divide between user and text
                if len(lines2) > 1:
                    speaker = lines2[0]
                    text = lines2[1]
                else:
                    speaker = ''
                    text = lines2[0]
                timestamp = lines[0]
            else:
                timestamp = ''
                speaker = ''
                text = lines[0]
            chat += [[timestamp, speaker, text]]
    return chat

Sip, setelah kita punya datanya, kita bisa geledah dan obrak-abrik isinya untuk dianalisis lebih lanjut.

Statistika dasar

Pertama-pertama. kita ingin tahu ada berapa banyak baris dalam percakapan.

date

Hmmm, ternyata ada sekitar 137 pesan per harinya. Lumayan aktif juga.

Kemudian kita ingin tahu siapa sih yang paling sering bunyi dan siapa yang paling senyap. Kita hitung total berapa banyak pesan yang ditulis per orangnya.

user frequency
Nama dan nomor telepon disamarkan untuk menjaga privasi

Wah, ternyata ada perbedaan yang cukup jomplang antara yang paling sering mengirim pesan dengan yang paling tidak sering mengirim pesan. Bahkan yang nomor 1 jumlah pesannya sekitar 2 kali dari yang nomor 2. Distribusi ini sepertinya mengikuti Zipf’s Law.

Frekuensi kata

Fitur utama dalam analisis dokumen biasanya adalah frekuensi kata. Model ini biasanya dikenal dengan nama Bag-of-Words. Kalau misalkan fitur ini dirasa kurang cukup ekspresif, biasanya ditambah lagi menjadi n-grams model, dimana kata dihitung kemunculannya bersama kata lain.

Dalam analisis dokumen juga biasanya terdapat kata-kata yang frekuensinya tinggi namun tidak memberi makna lebih kepada tulisan, seperti kata hubung dan teman-temannya. Kumpulan kata-kata tersebut sering disebut dengan istilah stopwords. Stopwords ini biasanya dihilangkan sebelum analisis agar proses lebih ringkas dan akurat. Saya memakai daftar kata stopwords dari https://sites.google.com/site/kevinbouge/stopwords-lists yang ternyata mengambil dari thesis “A Study of Stemming Effects on Information Retrieval in Bahasa Indonesia“.

Sekarang kita coba hitung frekuensi kata dalam chat minus stopwords. Histogram hasilnya bisa dilihat di bawah.

word frequency

Hmm, menarik. Ternyata kata-kata yang paling banyak keluar adalah kata sambung yang salah ketik seperti ‘yg‘ dan ‘d‘.  Kata-kata lain yang banyak muncul adalah nama panggilan ke penghuni grup yang lain. Selain itu, ternyata grup ini cukup suka tertawa, terlihat dari 3 kemunculan ekspresi tawa yaitu ‘hahaha‘, ‘wkwk‘, dan ‘haha‘. Selain itu, ternyata grup ini rajin olah raga juga, dilihat dari kata ‘badminton‘ yang muncul lebih dari 200 kali.

Emoji

if a picture paints a thousand words then why can’t I paint you“. Kurang lebih itulah sepenggal lagu If dari Bread. Penggunaan emoji atau emoticon sangat kentara dalam chatting karena satu gambarnya bisa mengekspresikan hal yang sulit jika dicoba disampaikan dengan kata-kata. Penggunaan emoji ini sangat pesat hingga menimbulkan sentimen kalau nanti kita bisa kembali ke zaman Mesir kuno dengan tulisan hieroglyph-nya. Karena umumnya penggunaan emoji ini, saya pun mencoba menghitung frekuensi dari tiap-tiap emoji.

Yang agak menyulitkan mungkin adalah emoji ini direpresentasikan dalam unicode dan di-encode dalam UTF-8 sehingga harus dibuat fungsi konversinya dulu antar keduanya. Gambar untuk plot didapat dari https://github.com/github/gemoji/ yang saya dapat link-nya dari apps.timwhitlock.info/emoji/tables/unicode. Kesulitan lain adalah ternyata Python Image Library punya sedikit untuk png dengan transparency. Apa boleh buat, gambar emoji-nya pun harus saya konversi ke jpg dengan utilitas mogrify dari imagemagick. Histogram hasilnya bisa dilihat di gambar di bawah.

emoji frequency

Hmm, menarik. Bisa dilihat, emoji teratas adalah senyum sambil berkeringat yang saya baca sebagai ekspresi dari ‘hehe’. Sisanya adalah berbagai ekspresi untuk senyum dan tertawa. Tetapi emoji ke 3 yang agak aneh. Mata. Ada apa dengan mata? Emoji ke 8, ‘see no evil monkey’ juga aneh. Apakah ada hubungannya dengan emoji ke 3?

Topik

Target berikutnya adalah Topic modelling. Lazimnya, setahu saya ini memakai teknik Latent Dirichlet Allocation (LDA). Karena saya belum pernah implementasi teknik ini sebelumnya dan kekurangan waktu untuk mencoba, saya mencoba mencari apakah ada library lain siap pakai. Saya ketemu situs startup untuk klasifikasi teks http://www.monkeylearn.com/. Di situs tersebut terdapat servis untuk melakukan klasifikasi topik dokumen secara general. Untuk free account diberikan 1000 kali panggilan API. Yah, cukuplah untuk proyek kecil-kecilan ini.

monkeylearn1monkeylearn2

Karena model-nya dibangun dari korpus Bahasa Inggris, maka teks harus diterjemahkan dulu ke bahasa tersebut. Hal ini sebenarnya bisa mengurangi akurasi, akan tetapi akurasi bukan tujuan utama yang dicari. Pilihan pertama saya, Google Translate API, ternyata tidak gratis. Ini tentunya cukup memberatkan saya yang cuma mahasiswa. Saya pun memakai layanan alternatif dari Yandex, search engine buatan Rusia.

topic

Dan inilah hasilnya. Ternyata percakapan di grup whatsapp ini dikategorikan ke ‘Entertainment & Recreation’. Hmm, bisa jadi. Yang agak aneh adalah kategori kedua, ‘Anime’, yang merupakan subkategori dari Entertainment di atas karena seingat saya tidak ada diskusi tentang anime sama sekali di grup. Hmm, inilah sulitnya memakai model buatan orang. Sulit untuk dianalisis.

Sebenarnya masih ada lagi yang mau saya coba, seperti menggunakan t-SNE untuk melihat kemiripan antar user. Namun, apa daya TTM (thesis telah memanggil).

Kode lengkap dapat dilihat di https://github.com/mitbal/wca. Cukup ikuti petunjuk di README untuk menjalankan program.

Semoga bermanfaat. Salam.

Pemilu Presiden Indonesia 2014: Pendekatan Pembelajaran Mesin

Pemilihan Umum Presiden Indonesia tahun 2014 sudah berakhir. Kalau boleh saya bilang ini pemilu presiden paling ramai yang pernah saya ikuti (walaupun saya belum ikut banyak pemilu sebenarnya, baru 2 kali sejak dapat KTP 7 tahun lalu). Orang berantem setiap hari di media sosial karena beda pilihan presiden (Facebook belum terlalu beken lima tahun lalu kayaknya). Yang bikin kurang sedap adalah banyak munculnya berita/tudingan/tuduhan yang kebenarannya tidak dapat dipertanggungjawabkan. Setelah saya pikir-pikir, mungkin hal-hal seperti ini ada baiknya juga sebagai bahan pembelajaran untuk kita semua warga Indonesia agar semakin cerdas dan dewasa, salah satunya dalam masalah politik. Nanti ke depannya, pada pemilu yang berikutnya misalnya, kita bisa memilih dan menyikap dengan lebih bijak.

Kebetulan saya kemarin bertugas menjadi salah satu petugas pelaksana pemilu luar negeri (PPLN) untuk negara Stockholm dan Latvia. Pemilu di luar negeri tentunya mempunyai nuansa yang agak berbeda dibandingkan di dalam negeri. Di luar negeri, penggunaan hak pilih dapat dilakukan melalui pos atau datang langsung ke TPS.

Jumlah pemilih di seluruh Swedia mencapai sekitar 700an. Untuk TPS, di Stockholm ada 1 TPS yang didirikan di Wisma Duta KBRI Stockholm. Alhamdulillah pemilu berjalan lancar sampai akhir. Hasil penghitungan akhir dapat dilihat di http://indonesiskaambassaden.se/hasil-pemilu/.

Persiapan dan pelaksanaannya sendiri tidak terlalu susah. Yang paling sulit mungkin adalah masalah pendataan pemilih yang datanya sudah tidak valid (sudah pindah atau ganti kewarganegaraan) atau tidak terdaftar sebagai pemilih karena tidak melapor ketika datang ke KBRI Swedia. Mungkin solusinya bisa menggunakan personnummer?

tps
TPS di Stockholm. Sepi ya, enggak kayak di Hong Kong.
20140909_120934
Perlengkapan pemilu untuk yang memilih lewat pos.

Ketatnya pemilu kali ini memunculkan wacana “kawal pemilu” untuk menjaga agar tidak terjadi kecurangan. Bak gayung bersambut, KPU ternyata juga menyediakan hasil scan formulir C1 yang berisi hasil penghitungan suara di situsnya https://pemilu2014.kpu.go.id/c1.php. Bermunculanlah karyakarya orang-orang kreatif dan inisiatif Indonesia di seantero dunia yang menyediakan sarana bagi masyarakat untuk merekapitulasi suara dari form C1. Fenomena ini dijabarkan dengan apik oleh dosen saya Pak Ruli di sini: http://theconversation.com/open-election-data-mass-interaction-indonesian-public-as-watchdog-29450.

Fenomena ini sangat positif, apalagi mungkin bagi para pakar e-government yang mendukung open data. Akan tetapi, karena bidang ilmu saya adalah Machine Learning, saya melihat upaya crowdsourcing ini dalam cahaya yang berbeda. Saya melihat ini adalah kesempatan untuk mendapatkan data dengan labelnya yang nantinya bisa dipakai untuk supervised learning.

Data adalah hal yang berlimpah ruah di era internet seperti sekarang. Akan tetapi, kebanyakan data-data yang tersedia tersebut bersifat unsupervised atau tidak ada label yang menunjukkan bahwa data tersebut termasuk ke kategori apa. Pada zaman dahulu kala, biasanya data harus diklasifikasikan manual oleh expert (Kalau contohnya pengkategorian objek pada gambar, maka manusia, asal punya akal sehat, menjadi expert-nya. Kalau misalkan data medis, maka harus dokter yang melakukan proses labelling).

Di era sekarang, biasanya dataset dibuat disusun dengan menggunakan jasa crowdsourcing berbayar seperti misalnya Amazon Mechanical Turk (walau tetap harus disupervisi). Contoh datasetnya misalkan Image-Net dan Microsoft COCO. Tapi tetap yang bisa membuat dataset adalah institusi riset yang mempunyai funding atau korporasi besar yang memiliki kapital yang besar juga (Coba bayangkan berapa biaya yang dikeluarkan Microsoft untuk mengumpulkan data hingga ratusan ribu gambar ketika mendesain algoritma deteksi skeleton untuk Kinect).

Oleh karena itu, ini adalah kesempatan emas untuk mendapatkan data tulisan tangan digit angka orang Indonesia dengan jumlah yang banyak. Dalam satu form C1 terdapat 12 angka. Jika dikalikan dengan jumlah TPS yang mencapai lebih dari 400 ribu maka total kita bisa mendapatkan data training sebanyak hampir 5 juta data. Sebagai pembanding, data handwritten digit yang sering dipakai untuk benchmarking performa algoritma machine learning baru, MNIST (walaupun ini sebenarnya hanya subset dari dataset yang lebih besar lagi ukurannya), memiliki jumlah 60.000 data untuk training dan 10.000 data untuk testing.

Berangkat dari motivasi di atas, maka saya memutuskan untuk mencoba mengekstraksi gambar tulisan angka dari gambar dan mengumpulkannya menjadi sebuah dataset kemudian melakukan eksplorasi dan eksperimen klasifikasi dengan machine learning sehingga nantinya mungkin bisa dibuat program yang otomatis akan menghitung rekap suara hanya dengan meng-upload gambar C1. Ini saya kerjakan pada liburan musim panas kemarin disambung sela-sela kuliah semester ganjil sekarang.

Akuisisi Data

Ada beberapa situs yang menyediakan jasa crowdsourcing yang bisa dipilih. Saya memilih kawalpemilu.org karena sepertinya ini yang paling lengkap (kawalpemilu.org menyelesaikan scan C1 dari 478.685 TPS yang tersebar di seluruh Indonesia dengan mengandalkan tenaga 700an relawan dalam waktu kira-kira lima hari) dan sepertinya datanya dapat dengan mudah diekstrak (halamannya cuma pakai tag table jadi saya kira cukup parsing halaman HTML saja). Saya putuskan saya harus men-download dulu semua file gambar dan rekap karena saya tidak tahu sampai kapan data tersebut akan dibuka.

Ekspektasi kedua saya ternyata salah karena situs kawalpemilu adalah satu halaman aplikasi javascript. Tetapi setelah menengok isi source code-nya, ternyata hasil rekap bisa dirikues dari webservice (yang di-host di Google Apps Engine) dalam format json. Dari source code saya juga menjadi tahu kalau scan C1 juga dapat dirikues ke server dengan cukup menyediakan nama file dengan format nomor daerah sebanyak 7 digit, nomor tps di daerah tersebut sebanyak 3 digit, dan angka yang menunjukkan halaman C1 yang diminta, dari 01 sampai 04 (yang diperlukan halaman keempat).

Kurang lebih potongan kode untuk men-download semua file adalah sebagai berikut (file scrap.py)

import requests as req

def prefix_pad(s, n):
    """ Prepend string s with 0 until it has length of n
    :param s: The string to be prepended
    :param n: The final length of prepended string
    :return: The prepended string
    """
    while len(s) < n:
        s = '0' + s
    return s

def image_filename(i, j):
    """ Prepare the KPU formatted image filename
    :param i: the area code
    :param j: the station code
    :return: The formatted filename
    """
    return prefix_pad(i, 7) + prefix_pad(j, 3) + '04'

# Loop for every area in Indonesia
start_area = 1
end_area = 80000
tps = 0
for i in xrange(start_area, end_area):
    print 'Now donwloading data from area: ', i
    r = req.get('http://kawal-pemilu.appspot.com/api/tps/' + str(i))

    if r.text == 'Error':
        continue

    json = r.json()
    for j in xrange(len(json)):
        # Check to see whether there is no error in json and also there is vote count data inside
        if json[j][6] and json[j][7] == '0':
            # Download C1 scanned image
            print 'TPS ke: ', tps
            imfname = image_filename(str(i), str(j + 1))
            imreq = req.get('http://scanc1.kpu.go.id/viewp.php?f=' + imfname + '.jpg')

            # Save to file
            f = open('scrap/' + str(i) + '_' + str(j) + '.txt', 'w')
            f.write('prabowo, jokowi, suara sah, tidak sah' + '\n')
            prabowo_count = str(json[j][2])
            jokowi_count = str(json[j][3])
            valid_count = str(json[j][4])
            invalid_count = str(json[j][5])
            f.write(prabowo_count + ',' + jokowi_count + ',' + valid_count + ',' + invalid_count)
            f.close()
            f = open('scrap/' + str(i) + '_' + str(j) + '.jpg', 'wb')
            f.write(imreq.content)
            f.close()
            tps += 1

Setelah kode tersebut dieksekusi, butuh waktu hingga 3 minggu untuk men-download semua file gambar dan rekap json. Programnya sendiri harus di-restart beberapa kali. Hasil akhirnya adalah File gambar dengan jumlah cukup banyak sekitar 417 ribu dengan ukuran total 60 gigabytes yang sementara tersimpan dengan aman di harddisk eksternal saya. Setelah melakukan bersih-bersih dengan menghapus data yang korup, sekarang kita bisa melakukan hal-hal menarik terhadap data yang kita punya.

c1
Contoh beberapa file scan form C1 yang diunduh. Hampir semua gambar memiliki ukuran, resolusi, dan kondisi yang beraneka ragam seperti terbalik atau tertukan dengan halaman lain. Hal-hal seperti ini yang mempersulit proses ekstraksi nantinya.

Ektraksi Data

Menentukan Region of Interest.

Untuk mempercepat proses komputasi dalam mengekstrak angka yang ada di dalam gambar, kita potong gambar hingga hanya menjadi region of interest-nya saja. Hal yang mempersulit adalah resolusi gambar yang berbeda-beda. Jika harus ditangani satu per satu maka kode-nya bisa meledak. Setelah melakukan investigasi singkat, kira-kira ada 3 jenis ukuran resolusi, kecil, sedang, dan tinggi. Saya buatlah aturan yang mudah-mudahan mengakomodir semua gambar.

    dim = c1.shape
    y0 = 350
    y1 = y0 + 450
    if dim[0] < 1100:
        y0 = dim[0] * 300 / 1700
        y1 = y0 + dim[0] * 400 / 1700
    elif 1700 < dim[0] < 2400:
        y0 = 350
        y1 = y0 + 450
    else:
        y0 = dim[0] * 350 / 1700
        y1 = y0 + dim[0] * 450 / 1700
    x0 = dim[1] * 19 / 24

Ekstraksi Digit

Setelah kita mendapatkan area yang mengandung rekap suara, proses ekstraksi dapat dilakukan dengan mudah dan cepat. Ada 2 pendekatan yang saya gunakan. Pertama, ekstraksi menggunakan Hough transform yang mengambil inspirasi dari sini sudokugrab.blogspot.se/2009/07/how-does-it-all-work.html. Pendekatan kedua menggunakan Harris corner detection.

Extraction based Hough transform

Pertama, gambar perlu di-threshold menggunakan Otsu threshold untuk mendapatkan edge dari gambar. Tidak perlu teknik sulit-sulit macam Canny edge detection, karena gambar relatif bebas dari noise dan tidak terlalu kompleks.

Kemudian kita panggil fungsi untuk melakukan Hough transform. Dari Hough space ini kita bisa mendapat semua garis yag ada di gambar. Dari semua garis yang didapat, kita seleksi 4 garis yang paling dekat dengan sisi terluar gambar (2 vertikal, 2 horizontal). Dari pasangan garis tadi kita hitung titik perpotongan antara garis vertikal dan horizontal untuk mendapat pojok terluar dari kotak rekap.

Setelah mendapat 4 pojok dari kotak, kita lakukan transformasi Affine. Transformasi ini bisa dalam bentuk skala, rotasi, dan translasi di mana garis paralel sebelum transformasi akan tetap paralel setelah transformasi. Kita transformasi agar orientasinya lurus dan ukurannya seragam semua 400 x 150. Setelah ditransformasi cukup dipotong kotak-kotak sebanyak 12.

ekstrak digit
Tahapan proses, dimulai dari region of interest, thresholding, hough transform, dan terakhir transformasi.

 

Extraction based Harris corner

Berbeda dengan pendekatan menggunakan Hough transform, dengan corner detection kita sudah otomatis mendapatkan titik-titik yang potensial menjadi ujung dari kotak rekap suara tanpa harus melewati pencarian garis terlebih dahulu. Selanjutnya cukup mencari titik yang terdekat dengan keempat ujung gambar. Transformasi yang sama juga dilakukan setelah itu.

Akan tetapi setelah melihat hasil empiris, maka saya lebih memilih teknik Hough transform karena file gambar hasil akhir lebih rapi dibanding dengan corner detection. Hal ini ditengarai karena corner dapat terdeteksi pada posisi-posisi yang salah dan ini mengakibatkan angka setelah ditransformasi menjadi terdistorsi.

Batch process

Setelah sukses melakukan ekstraksi pada satu gambar C1, berikutnya proses ekstraksi  akan dijalankan ke semua gambar scan C1. Niatnya  awalnya ke semua gambar, tapi saya jalankan untuk 100.000 data pertama dahulu.

Hasil akhirnya ratusan ribu gambar angka berhasil terekstrak. Tetapi ternyata banyak gambar-gambar yang rusak karena salah ekstraksi (region of interest-nya salah sehingga kotak yang berisi angka tidak berhasil ditemukan atau terpotong). Oleh karena itu masih diperlukan seleksi secara manual (walaupun sebenarnya cepat untuk dilakukan, cukup Ctrl+X Ctrl+V, tapi membosankan untuk dilakukan). Hasil sementara ada 11 kelas (0 sampai 9 tambah kelas khusus X untuk digit ratusan atau puluhan yang nilai tidak sampai situ) dengan masing-masing kelas mempunyai 2000 data. Masih ada banyak lagi data yang bisa diambil, mungkin solusinya perlu di-crowdsourcing juga.

Semua proses di atas ada dalam modul extract.py di repositori.

digit.pn
Contoh subset dataset dengan 100 data per kelas. Datanya lumayan variatif kan…

Training & Testing

Setelah kita mendapatkan data per kelas yang cukup banyak kita bisa memulai proses training. Untuk proses ini saya menggunakan framework machine learning favorit saya, Weka. Weka men-support banyak algoritma serta memiliki kemampuan untuk menyimpan dan memuat model hasil pelatihan sebelumnya. Untuk eksperimen awal ini, kita coba menggunakan data yang masih raw dan classifier nearest neighbor (di weka namanya adalah IB1). Kita coba pada dataset yang kecil terlebih dahulu, dengan ukuran 500 instance per kelas.

weka load data
500 data per kelas, masing-masing data memiliki 5000 fitur.

Dengan skema 10-fold cross validation, akurasi yang didapatkan adalah sekitar 60%. Hmm, tidak terlalu buruk. Tentunya hasil akurasi di atas bisa ditingkatkan lebih lanjut, dengan menggunakan teknik-teknik ekstraksi fitur yang lebih canggih atau menggunakan classifier yang lebih powerful dan dilakukan fine tuning hingga hanya memiliki error yang sangat kecil.

weka run
Hasil klasifikasi dalam bentuk akurasi dan confusion matrix.

Terakhir, kita buat tampilan grafik yang digunakan untuk memuat dan melakukan ekstraksi dan klasifikasi digit dengan model yang telah didapatkan. GUI dibuat dengan Tkinter.

gui
Tampilan GUI awal; setelah me-load scan lembar C1 dan model; serta setelah diproses.

Kalau dilihat-lihat hasilnya ada salah prediksi untuk angka 2 dan karakter X. Yah, lumayanlah…

Penutup

Demikian kira-kira bagaimana ilmu machine learning bisa ikut-ikutan pada hajatan pemilu 2014. Kalau kawalpemilu butuh 5 hari untuk merekap semua suara, maka dengan bantuan komputer seharusnya itu bisa selesai lebih cepat lagi. Jika ada yang mempunyai ide berbeda untuk tahap pengekstrakan agar lebih efektif, bisa langsung dengan me-fork repositori dan langsung implementasi atau langsung kirim email untuk berdiskusi.

Untuk file dataset dapat di-download di http://db.orangedox.com/ontgRUzd9EjjlxZVoD/11c_2000i.zip. Kalau perlu gunakan module prepare_input.py untuk membaca file gambar dan menjadikannya ke file CSV yang siap diproses di Weka.

Rencananya akan saya update begitu saya bisa memisahkan lebih banyak data lagi per kelasnya. Sementara kode untuk semua bisa dilihat di repository github: https://github.com/mitbal/pemilu

Post-post berikutnya kita akan membahas metode ekstraksi fitur dan klasifikasi yang lebih powerful. Mudah-mudahan bisa coba sampai metode representasi fitur yang state-of-the-art macam fitur CNN.

Semoga berguna. Salam.

Visualisasi dengan Processing

Pada post ini saya akan memperkenalkan processing. Processing adalah bahasa pemrograman yang digunakan untuk membuat visualisasi dalam bentuk gambar statis dan/atau animasi. Secara bahasa pemrograman, Processing memiliki sintaks yang mirip dengan Java.

logo

Instalasi

Masuk ke halaman Download di situs processing dan pilih file sesuai dengan sistem operasi yang Anda gunakan.

download

Ekstrak file zip ke direktori manapun yang dikehendaki.

Getting Started

Buka folder hasil ekstraksi dan jalankan processing.exe

processing

Gambar di atas adalah tampilan awal dari processing ketika baru dijalankan. Pada text editor, ketikkan kode berikut untuk menginisialisasi visualisasi baru lalu klik tombol Rum (yang gambar segitiga) atau lewat menu Sketch > Run atau dengan shortcut Ctrl+R.

void setup() {
  size(500, 400);
}

void draw() {
  background(0,0,255);
}

Seharusnya keluar window baru dengan warna latar belakang biru. Kode di atas merupakan kode paling dasar pada Processing. fungsi setup() dijalankan sekali pada waktu program mulai berjalan di awal sedangkan fungsi draw() dipanggil berulang kali dalam satu detik. Berapa kali fungsi draw() dipanggil bisa dikontrol dengan fungsi frameRate() di mana nilai standarnya adalah 60 fps.

Contoh: Bacteria random walk

Untuk contoh yang sedikit lebih rumit dibanding hanya background kosong, kita akan membuat animasi bakteri yang berjalan secara acak.
Pertama, kita buat kelas baru untuk menyimpan data posisi bakteri. Jangan lupa fungsi constructor untuk menginisialisasi posisi awal bakteri.

class Bactery {
  int x;
  int y;
  float angle;
  
  Bactery() {
    x = int(random(width));
    y = int(random(height));
    angle = 2*PI*random(1);
  }
  
  Bactery(int x, int y) {
    this.x = x;
    this.y = y;
    angle = 0;
  }
}

Kemudian kita tambahkan fungsi display() untuk menggambar bakteri, dimana kita sederhanakan bentuk bakteri menjadi sebuah segi empat.

void display() {
    stroke(0);
    fill(0);
    
    if(random(1) < 0.2)
      angle = 2*PI*random(1);
      
    int x1 = bactWidth;
    int y1 = 0;
    
    int x2 = bactWidth;
    int y2 = bactHeight;
    
    int x3 = 0;
    int y3 = bactHeight;
    
    // Apply the rotation
    int nx1 = int( x1*cos(angle) - y1*sin(angle) +x);
    int ny1 = int( x1*sin(angle) + y1*cos(angle) +y);
    
    int nx2 = int( x2*cos(angle) - y2*sin(angle) +x);
    int ny2 = int( x2*sin(angle) + y2*cos(angle) +y);
    
    int nx3 = int( x3*cos(angle) - y3*sin(angle) +x);
    int ny3 = int( x3*sin(angle) + y3*cos(angle) +y);
    
    quad(x, y, nx1, ny1, nx2, ny2, nx3, ny3);
  }

Tambahkan pula fungsi step() untuk meng-update posisi bakteri pada setiap frame.

void step() {
    int moveX = int(random(11)) - 5;
    int moveY = int(random(11)) - 5;
    
    x += moveX;
    y += moveY;
  }

Instansiasi objek bakteri di awal, kemudian kita panggil kedua fungsi diatas pada setiap frame.

void setup() {
  size(500, 400);
  
  bacteria = new Bactery[numBact];
  for(int i=0; i<numBact; i++) {
    bacteria[i] = new Bactery();
  }
}

void draw() {
  background(255,255,255);
  
  for(int i=0; i<numBact; i++) {
    bacteria[i].step();
    bacteria[i].display();
  }
}

Jangan lupa untuk menambahkan variabel-variabel global yang dipakai.

Bactery[] bacteria;
int numBact = 200;
int bactWidth = 2;
int bactHeight = 10;

Contoh tampilan ketika dijalankan: bakteri yang jumlahnya banyak menggeliat sepanjang waktu.

bakteri

Processing on the Web

dengan menggunakan processing.js, program processing Anda akan secara ajaib dan otomatis bisa ditampilkan pada halaman web dengan memanfaatkan teknologi HTML5. Cukup dengan membuat sebuah halaman html kosong, import processingjs, dan tambahkan canvas ke dalamnya.

<script src="https://raw.github.com/processing-js/processing-js/v1.4.8/processing.min.js"></script>
<canvas data-processing-sources="bacterium.pde"></canvas>

Karena wordpress.com tidak bisa pakai iframe, maka animasinya bisa dilihat pada link berikut: http://mitbal.com/bacterium.html

Seperti biasa kode lengkap bisa dilihat di repository github: https://github.com/mitbal/pro-bacterium

Demikian, semoga berguna. Sampai ketemu pada contoh-contoh visualisasi lainnya.
Salam.

Baseline Wander Removal dengan Wavelet

Pada post kali ini, kita akan melakukan proses menghilangkan baseline wander (baseline wander removal) pada sinyal EKG dengan menggunakan transformasi wavelet seperti yang ditulis oleh Sargolzaei et al pada paper dengan judul “A new robust wavelet based algorithm for baseline wandering cancellation in ECG signals“.

Latar belakang
Elektrokardiogram (EKG) adalah pembacaan sinyal elektrik jantung dengan cara menempelkan elektroda ke posisi tertentu pada tubuh dan kemudian membaca nilai perbedaan potensial listrik alias tegangan yang dihasilkan. Dari pembacaan ini kita bisa mencari tahu mengenai kondisi jantung. Akan tetapi, sinyal yang baru dibaca ini tidak lepas dari noise sehingga bisa mengganggu proses diagnosis. Sumber noise ini berasal dari berbagai macam, seperti elektroda dari elektroda itu sendiri atau jika tubuh bergerak ketika dilakukan pengukuran. Salah satu kondisi noise yang sering menyerang adalah situasi yang disebut baseline wander. Baseline wander terjadi apabila sinyal EKG tidak lurus pada sumbu x, malah naik turun. Contoh bisa dilihat di gambar 1.

bwr
Contoh sinyal yang terkena baseline wander dan hasilnya setelah dihilangkan. Gambar diambil tanpa izin dari paper yang dirujuk di atas.

Baseline wander removal (BWR) adalah salah satu tahap preprocessing pada sinyal EKG untuk menghilangkan baseline drift ini. Ada beberapa teknik yang bisa dipakai, seperti melakukan filtering. Pada post ini, kita akan mengimplementasikan salah satu teknik yang memanfaatkan transformasi wavelet. Penjelasan dasar mengenai wavelet salah satunya bisa dilihat di http://users.rowan.edu/~polikar/WAVELETS/WTtutorial.html. Kelebihan dari teknik ini adalah kita tidak perlu menentukan parameter seperti ketika menggunakan high pass filter (frequency cut-off) sehingga metode kita bekerja secara non-supervised.

Sinyal EKG yang sudah bersih dari noise semacam ini kemudian bisa digunakan untuk berbagai macam kegunaan. Untuk pembahasan mengenai kegunaan sinyal EKG untuk mengenali tipe detak arrhythmia atau diagnosis tahapan tidur, bisa merujuk ke paper ini dan ini.

Algoritma
Berikut adalah tahapan dalam algoritma ini. Pertama, kita lakukan dekomposisi kepada sinyal asli dengan transformasi wavelet. Dalam hal ini kita memilih Daubechies orde 4 sebagai fungsi basisnya. Sinyal didekomposisi menjadi bagian frekuensi rendah/aproksimasi dan frekuensi tinggi/detail. Kemudian kita hitung nilai energi pada sinyal frekuensi tinggi. Kita cari kondisi dimana nilai energi pada level dekomposisi tersebut lebih rendah dari pada nilai pada level dekomposisi sebelumnya dan sesudahnya (alias lokal minima). Setelah kita temukan level tersebut, kita rekonstruksi sinyal aproksimasi dari level ini dengan membuang nilai pada sinyal frekuensi tinggi (atau dijadikan 0 semua). Sinyal hasil rekonstruksi ini kita sebut dengan baseline. Untuk menghilangkan baseline wander pada sinyal asli, maka kita kurangkan sinyal asli dengan sinyal baseline.

level
Gambar atas: plot nilai energi pada sinyal detail pada berbagai level dekomposisi. Tanda panah menunjukkan level ketika nilainya adalah lokal minima. Gambar bawah: Sinyal asli, baseline, dan sinyal asli yang sudah dikurang baseline. Lagi-lagi diambil dari paper rujukan di atas.

 

Implementasi
Pertama, kita implementasi metode konvolusi antara dua sinyal.

def conv(x, h):
    """ Perform the convolution operation between two input signals. The output signal length
    is the sum of the lenght of both input signal minus 1."""
    length = len(x) + len(h) - 1
    y = [0]*length

    for i in xrange(len(y)):
        for j in xrange(len(h)):
            if i-j >= 0 and i-j < len(x):
                y[i] += h[j] * x[i-j]

    return y

Lalu kita implementasi metode dekomposisi wavelet dan jangan lupa deklarasi koefisien basis fungsi yang dipakai. Dalam kasus ini adalah Daubechies dengan 4 koefisien. Dekomposisi dilakukan dengan cara mengkonvolusi sinyal asli dengan koefisien low pass and high pass. Sinyal keluaran dari proses ini kemudian di-downsampling menjadi setengahnya dengan cara hanya mengambil nilai pada posisi ganjil atau genap saja. Hal ini dilakukan berulang terhadap sinyal keluaran low pass (atau disebut aproksimasi) sebanyak parameter level.

c0 = (1+sqrt(3))/(4*sqrt(2))
c1 = (3+sqrt(3))/(4*sqrt(2))
c2 = (3-sqrt(3))/(4*sqrt(2))
c3 = (1-sqrt(3))/(4*sqrt(2))

def db4_dec(x, level):
""" Perform the wavelet decomposition to signal x with Daubechies order 4 basis function as many as specified level"""

    # Decomposition coefficient for low pass and high pass
    lpk = [c0, c1, c2, c3]
    hpk = [c3, -c2, c1, -c0]

    result = [[]]*(level+1)
    x_temp = x[:]
    for i in xrange(level):
        lp = conv(x_temp, lpk)
        hp = conv(x_temp, hpk)

        # Downsample both output by half
        lp_ds=[0]*(len(lp)/2)
        hp_ds=[0]*(len(hp)/2)
        for j in xrange(len(lp_ds)):
            lp_ds[j] = lp[2*j+1]
            hp_ds[j] = hp[2*j+1]

        result[level-i] = hp_ds
        x_temp = lp_ds[:]

    result[0] = lp_ds
    return result

Fungsi rekontruksi digunakan untuk mencari baseline dari sinyal asal. Rekonstruksi bekerja dengan melakukan konvolusi dengan koefisien rekonstruksi pada kedua sinyal low pass dan high pass, melakukan upsampling dengan menyelipkan 0 di setiap nilai pada sinyal dan kemudian masing-masing posisi pada sinyal saling dijumlahkan.

def db4_rec(signals, level):
    """ Perform reconstruction from a set of decomposed low pass and high pass signals as deep as specified level"""

    # Reconstruction coefficient
    lpk = [c3, c2, c1, c0]
    hpk = [-c0, c1, -c2, c3]

    cp_sig = signals[:]
    for i in xrange(level):
        lp = cp_sig[0]
        hp = cp_sig[1]

        # Verify new length
        length = 0
        if len(lp) > len(hp):
            length = 2*len(hp)
        else:
            length = 2*len(lp)

        # Upsampling by 2
        lpu = [0]*(length+1)
        hpu = [0]*(length+1)
        index = 0
        for j in xrange(length+1):
            if j%2 != 0:
                lpu[j] = lp[index]
                hpu[j] = hp[index]
                index += 1

        # Convolve with reconstruction coefficient
        lpc = conv(lpu, lpk)
        hpc = conv(hpu, hpk)

        # Truncate the convolved output by the length of filter kernel minus 1 at both end of the signal
        lpt = lpc[3:-3]
        hpt = hpc[3:-3]

        # Add both signals
        org = [0]*len(lpt)
        for j in xrange(len(org)):
            org[j] = lpt[j] + hpt[j]

        if len(cp_sig) > 2:
            cp_sig = [org]+cp_sig[2:]
        else:
            cp_sig = [org]

    return cp_sig[0]

Method calcEnergy menghitung nilai energi dari sebuah sinyal berdasarkan definisinya yaitu jumlahan kuadrat sinyal di tiap titik

def calcEnergy(x):
    """ Calculate the energy of a signal which is the sum of square of each points in the signal."""
    total = 0
    for i in x:
        total += i*i
    return total

Kemudian method bwr berikut adalah implementasi dari algoritma yang dijabarkan di atas.

def bwr(raw):
    """ Perform the baseline wander removal process against signal raw. The output of this method is signal with correct baseline
    and its baseline """
    en0 = 0
    en1 = 0
    en2 = 0
    n = 0

    curlp = raw[:]
    num_dec = 0
    last_lp = []
    while True:
        print 'Iterasi ke' + str(num_dec+1)
        print len(curlp)

        # Decompose 1 level
        [lp, hp] = db4_dec(curlp,1)

        # Shift and calculate the energy of detail/high pass coefficient
        en0 = en1
        en1 = en2
        en2 = calcEnergy(hp)
        print en2

        # Check if we are in the local minimum of energy function of high-pass signal
        if en0 > en1 and en1 < en2:
            last_lp = curlp
            break

        curlp = lp[:]
        num_dec = num_dec+1

    # Reconstruct the baseline from this level low pass signal up to the original length
    base = last_lp[:]
    for i in xrange(num_dec):
        base = db4_rec([base,[0]*len(base)], 1)

    # Correct the original signal by subtract it with its baseline
    ecg_out = [0]*len(raw)
    for i in xrange(len(raw)):
        ecg_out[i] =  raw[i] - base[i]

    return (base, ecg_out)

Contoh
Untuk contoh kita ambil data dari situs Physionet khususnya MIT-BIH Arrhythmia database. Data diambil dari salah satu record pasien dengan kode nomor 101. Sinyal EKG diambil sepanjang 1 menit. Sinyal EKG yang diambil berasal dari lead II dan V5.

Contoh kode untuk memanggil modul dan fungsi yang sudah dibuat di atas adalah sebagai berikut

import bwr
import matplotlib.pyplot as plt

# Read input csv file from physionet
f = open('samples1.csv', 'r')
lines = f.readlines()
f.close()

# Discard the first two lines because of header. Takes either column 1 or 2 from each lines (different signal lead)
signal = [0]*(len(lines)-2)
for i in xrange(len(signal)):
	signal[i] = float(lines[i+2].split(',')[1])

# Call the BWR method
(baseline, ecg_out) = bwr.bwr(signal)

plt.subplot(2,1,1)
plt.plot(signal, 'b-')
plt.plot(baseline, 'r-')

plt.subplot(2,1,2)
plt.plot(ecg_out, 'b-')
plt.show()

Berikut contoh pertama. Garis merah pada plot merupakan baseline dari sinyal EKG.

samples1

Berikut adalah contoh kedua.

samples2

Contoh berikut diambil dari lead V5.

samples3

Kode lengkap dan sampel sinyal dapat dilihat di: https://github.com/mitbal/py-bwr

Semoga berguna. Salam.

UPGMA

Pada post kali ini kita akan mengimplementasi algoritma UPGMA atau Unweighted Pair Group Method with Arithmetic Mean untuk melakukan clustering.

Algoritma
UPGMA bekerja dengan prinsip yang sederhana. Pada setiap iterasi, pilih pasangan point dengan point, atau point dengan cluster, atau cluster dengan cluster, yang memiliki jarak yang terpendek. Gabung kedua pasangan ini kedalam satu cluster. Hal ini dilakukan terus menerus hingga jumlah cluster berkurang menjadi jumlah yang diinginkan. Untuk menghitung jarak antar datapoint, kita bisa menggunakan berbagai kriteria jarak. Pada implementasi ini, kita menggunakan euclidean distance untuk menghitung jarak. Sedangkan untuk menghitung jarak antar cluster, kita hitung dengan cara merata-ratakan jarak antar semua pasang point dari cluster pertama ke cluster kedua, atau dengan kata lain:

\frac{1}{|A|.|B|} \sum_{x \in A} \sum_{y \in B} d(x,y)

Teknik UPGMA ini bisa digolongkan kepada teknik hierarchical clustering karena kita bisa melihat hirarki sebuah cluster yang dibentuk dari cluster-cluster lain yang lebih kecil.

Implementasi
Kita implementasikan UPGMA dalam bahasa Python. Pertama, kita butuh struktur data untuk menyimpan point yang terdapat pada sebuah cluster.

class Node:
    def __init__(self, p):
        self.points = p
        self.right = None
        self.left = None

Fungsi UPGMA menerima dua parameter. Parameter pertama adalah set yang berisi datapoints sebanyak n dengan masing-masing point memiliki dimensi d. Parameter kedua adalah jumlah cluster yang kita inginkan.

def upgma(points, k):
    """ Cluster based on distance matrix dist using Unweighted Pair Group Method with Arithmetic Mean algorithm up to k cluster"""

    # Initialize each cluster with one point
    nodes = []
    n = len(points)
    for i in xrange(n):
        node = Node([points[i]])
        nodes = nodes + [node]

    # Iterate until the number of clusters is k
    nc = n
    while nc > k:
        # Calculate the pairwise distance of each cluster, while searching for pair with least distance
        c1 = 0; c2 = 0; i1 = 0; i2 = 0;
        sdis = 9999999999
        for i in xrange(nc):
            for j in xrange(i+1, nc):
                dis = euclidistance(nodes[i], nodes[j])
                if dis < sdis:
                    sdis = dis
                    c1 = nodes[i]; c2 = nodes[j];
                    i1 = i; i2 = j;
        # Merge these two nodes into one new node
        node = Node(c1.points + c2.points)
        node.left = c1; node.right = c2;
        
        #Remove the previous nodes, and add the new node
        new_nodes = []
        for i in xrange(nc):
            if i != i1 and i != i2:
                new_nodes = new_nodes + [nodes[i]]
        new_nodes = new_nodes + [node]
        nodes = new_nodes[:]
        nc = nc - 1

    return nodes

Kemudian terakhir, kita definisikan fungsi jarak yang kita pakai. Selain jarak euclidean, kita bisa yang digunakan kriteria jarak lainnya seperti Manhattan distance atau Chebyshev distance.

import math

def euclidistance(c1, c2):
    """ Calculate the distance between two cluster """
    dist = .0
    n1 = len(c1.points)
    n2 = len(c2.points)
    for i in xrange(n1):
        for j in xrange(n2):
            p1 = c1.points[i]
            p2 = c2.points[j]
            dim = len(p1)
            d = 0
            for k in xrange(dim):
                d = d + (p1[k]-p2[k])**2
            d = math.sqrt(d)
            dist = dist + d
    dist = dist / (n1*n2)
    return dist

Contoh
Untuk contoh pertama kita pakai dataset sintesis yang dihasilkan oleh dua buah distribusi normal multidimensi.

import upgma
import random
import matplotlib.pyplot as plt
import math

# Example 1
datapoints = [(random.normalvariate(2.5, 1.0), random.normalvariate(1.5,1.0)) for i in xrange(100)] + \
				[(random.normalvariate(-1, 0.5), random.normalvariate(3,0.5)) for i in xrange(100)]

# Plot datapoints before clustering
plt.plot([x for (x,y) in datapoints], [y for (x,y) in datapoints], 'k^')
plt.show()

# Cluster the data
nodes = upgma.upgma(datapoints, 2)
plt.plot([x[0] for x in nodes[0].points], [x[1] for x in nodes[0].points], 'b*')
plt.plot([x[0] for x in nodes[1].points], [x[1] for x in nodes[1].points], 'ro')
plt.show()

dan berikut keluaran hasil clustering jika kita pilih 2 sebagai jumlah cluster.
nocluster1cluster1

Contoh kedua kita ambil dataset old faithful geyser (http://www.stat.cmu.edu/~larry/all-of-statistics/=data/faithful.dat).

f = open('faithful.dat.txt', 'r')
lines = f.readlines()
f.close()

datapoints = []
for line in lines:
	tokens = line.split()
	datapoints += [(float(tokens[1]), float(tokens[2]))]

Karena fitur kedua berbeda skalanya dengan fitur pertama (puluhan berbanding dengan satuan), kita lakukan normalisasi z-score pada tiap masing-masing fitur dengan cara mengurangi dengan mean distribusi dan kemudian dibagi dengan standar deviasi.

avg1 = sum([x for (x,y) in datapoints])
avg2 = sum([y for (x,y) in datapoints])
centered_datapoints = map(lambda (x,y): (x-avg1, y-avg2), datapoints)
std1 = math.sqrt(sum(map(lambda x: x*x, [x for (x,y) in centered_datapoints])))
std2 = math.sqrt(sum(map(lambda x: x*x, [y for (x,y) in centered_datapoints])))
normalized_datapoints = map(lambda (x,y): (x/std1, y/std2), centered_datapoints)

Hasil clustering-nya adalah sebagai berikut.

# Before clustering
plt.plot([x for (x,y) in normalized_datapoints], [y for (x,y) in normalized_datapoints], 'k^')
plt.show()

# Cluster the data
nodes = upgma.upgma(normalized_datapoints, 2)
plt.plot([x[0] for x in nodes[0].points], [x[1] for x in nodes[0].points], 'b*')
plt.plot([x[0] for x in nodes[1].points], [x[1] for x in nodes[1].points], 'ro')
plt.show()

nocluster2cluster2

Kode lengkap dapat dilihat di: https://github.com/mitbal/py-upgma

Semoga berguna. Salam.

Transformasi Gambar Buta Warna

Pada post kali ini, kita akan menyimulasikan gambar yang kira-kira dilihat oleh mereka yang memiliki kekurangan penglihatan dalam hal membedakan warna atau buta warna dengan mengikuti metode yang diajukan oleh paper dengan judul Digital Video Colourmaps for Checking the Legibility of Display by Dichromats oleh Vienot et al. Hal ini tentunya akan berguna jika kita akan mendesain website atau papan pengumuman atau poster sehingga semua karya kita bisa dinikmati oleh semua orang.

Latar belakang

Pada retina di mata, manusia memiliki 2 jenis sel, yaitu sel batang (rod cell) dan sel kerucut (cone cell). Sel batang berguna untuk mengetahui tingkat kecerahan cahaya (brightness) dan berfungsi dengan baik ketika pencahayaan kurang seperti pada malam hari. Sedangkan sel kerucut bekerja ketika terdapat pencahayaan yang cukup dan berfungsi untuk membedakan warna. Terdapat tiga jenis sel kerucut yang masing-masing memiliki tingkat kesensitifan yang berbeda terhadap panjang gelombang warna, sel kerucut merah, hijau, dan biru. Persentase komposisinya kurang lebih 65% merah, 33% hijau, dan 2% biru.

Makhluk hidup yang memiliki 3 jenis sel kerucut dengan sensitivitas berbeda disebut trichromats. Beberapa hewan karnivora seperti anjing hanya memiliki dua jenis sel kerucut, alias dichromats. Sedangkan burung dan beberapa jenis ikan memiliki 4 jenis sel kerucut yang berbeda sehingga bisa disebut tetrachromats.

Buta warna sendiri adalah kondisi ketika manusia kekurangan satu jenis sel kerucut. Buta warna diperkirakan menimpa 8% dari populasi laki-laki di seluruh dunia. Ada dua jenis buta warna yang akan disimulasikan pada post ini, protan dan deutan. Protan terjadi karena mata hanya memiliki sel kerucut hijau dan biru sedangkan Deutan terjadi ketika mata hanya memiliki sel kerucut merah dan biru.

Implementasi

Berikut kode dalam Matlab.

function [ pim, dim ] = MakeDichromatIms( im )
%This function transform the input image into two different images how they
%perceived by dichromats people (protan and deutan)

    im = double(im);

    % Normalize transform the original RGB value
    im1 = (im/255).^(2.2);
    R = im1(:, :, 1);
    G = im1(:, :, 2);
    B = im1(:, :, 3);

    % Protan transform
    pR = 0.992052*R + 0.003974;
    pG = 0.992052*G + 0.003974;
    pB = 0.992052*B + 0.003974;

    % Deutan transform
    dR = 0.957237*R + 0.0213814;
    dG = 0.957237*G + 0.0213814;
    dB = 0.957237*B + 0.0213814;

    % Transformation matrix from RGB to LMS
    M = [17.8824 43.5161 4.11935;
         3.45565 27.1554 3.86714;
         0.0299566 0.184309 1.46709];

    % Protan projection
    P = [0 2.02344 -2.52581; 0 1 0; 0 0 1];

    % Deutan projection
    D = [1 0 0; 0.494207 0 1.24827; 0 0 1];

    % Precompute the transformation matrix before loop to speed things up
    Tp = inv(M)*P*M;
    Td = inv(M)*D*M;

    % Allocate the output image
    pim = zeros(size(im));
    dim = zeros(size(im));

    % Apply the transformation for each pixel
    for ii=1:size(im, 1)
       for jj=1:size(im, 2)
           protan = Tp*([pR(ii,jj) pG(ii,jj) pB(ii,jj)]');
           deutan = Td*([dR(ii,jj) dG(ii,jj) dB(ii,jj)]');

           pim(ii, jj, 1) = protan(1); pim(ii, jj, 2) = protan(2); pim(ii, jj, 3) = protan(3);
           dim(ii, jj, 1) = deutan(1); dim(ii, jj, 2) = deutan(2); dim(ii, jj, 3) = deutan(3);
       end
    end

    % Reverse the initial transform
    pim = 255*(pim.^(1/2.2));
    dim = 255*(dim.^(1/2.2));
end

–Edit–
Berikut implementasi yang memanfaatkan perkalian matrix di Matlab sehingga waktu eksekusinya 10x lebih cepat dibanding kode di atas.

function [ pim, dim ] = MakeDichromatIms2( im )
%This function transform the input image into two different images how they
%perceived by dichromats people (protan and deutan) faster using matrix
%operation in Matlab

    im = double(im);
    
    % Normalize transform the original RGB value
    im1 = (im/255).^(2.2);
    R = im1(:, :, 1);
    G = im1(:, :, 2);
    B = im1(:, :, 3);
    
    % Protan transform
    pR = 0.992052*R + 0.003974;
    pG = 0.992052*G + 0.003974;
    pB = 0.992052*B + 0.003974;
    
    % Deutan transform
    dR = 0.957237*R + 0.0213814;
    dG = 0.957237*G + 0.0213814;
    dB = 0.957237*B + 0.0213814;
    
    % Transformation matrix from RGB to LMS
    M = [17.8824 43.5161 4.11935; 
         3.45565 27.1554 3.86714; 
         0.0299566 0.184309 1.46709];
    
    % Protan projection
    P = [0 2.02344 -2.52581; 0 1 0; 0 0 1];
    
    % Deutan projection
    D = [1 0 0; 0.494207 0 1.24827; 0 0 1];

    % Precompute the transformation matrix before loop to speed things up
    Tp = inv(M)*P*M;
    Td = inv(M)*D*M;
    
    % Apply the transformation using matrix operation
    PP = Tp * [pR(:)'; pG(:)'; pB(:)'];
    DD = Td * [dR(:)'; dG(:)'; dB(:)'];
    
    % Reverse the initial transform
    PP = 255*(PP.^(1/2.2));
    DD = 255*(DD.^(1/2.2));
    
    % Reshape the computed vector back
    pim = zeros(size(im));
    pim(:,:, 1) = reshape(PP(1, :), size(R));
    pim(:,:, 2) = reshape(PP(2, :), size(G));
    pim(:,:, 3) = reshape(PP(3, :), size(B));
    
    dim = zeros(size(im));
    dim(:,:, 1) = reshape(DD(1, :), size(R));
    dim(:,:, 2) = reshape(DD(2, :), size(G));
    dim(:,:, 3) = reshape(DD(3, :), size(B));
end

Contoh keluaran dari program.

firetruck

flowers

Semoga berguna. Salam

Gibbs Sampling

Pada post ini, saya akan menjelaskan mengenai implementasi algoritma Gibbs sampling untuk mendeteksi pola pada deret DNA atau populer dengan istilah motif finding seperti yang dijabarkan oleh Lawrence di paper-nya pada tahun 1993 Detecting Subtle Sequence Signals: A Gibbs Sampling Strategy for Multiple Alignment.

Sekilas mengenai Gibbs sampling. Gibbs sampling adalah salah satu algoritma keluarga dari Markov Chain Monte Carlo (MCMC). Kurang lebih intinya adalah kita bisa menghitung joint probability distribution dengan cara melakukan sampling satu per satu terhadap setiap variabel dengan berdasarkan nilai variabel lainnya alias full conditional probability. Materi pengenalan Gibbs sampling yang lebih lanjut bisa dipelajari di sini dan di sini.

Motif itu sendiri adalah pola yang terdapat pada beberapa deret DNA yang memiliki tingkat kemiripan yang tinggi. Contohnya bisa dilihat pada gambar di bawah. Seperti yang bisa dilihat, terdapat kemunculan pola dengan bentuk CAGGAT pada 3 deret dengan lokasi yang berbeda-beda. Motif finding bertujuan menemukan pola tersebut berdasarkan data semua deret string yang dimiliki. Karakter yang muncul selain pada motif biasa disebut background.

motif

Nah, bagaimana caranya kita bisa menggunakan Gibbs sampling untuk melakukan deteksi motif? Ada 2 asumsi sebelum kita memulai memakan Gibbs sampling. Pertama, pada satu deret DNA hanya ada 1 buah pola dan kedua, kita mengetahui berapa panjang dari pola tersebut. Kita akan implementasi Gibbs sampling dengan menggunakan bahasa pemrograman Python.

Pertama, kita inisialisasi state posisi secara acak

pos = [randint(0, len(seq)-w+1) for seq in sequences]

Kemudian kita lakukan iterasi sampai posisi yang kita cari konvergen. Kita lakukan iterasi terhadap semua deret satu per satu. Sebutlah deret yang aktif pada iterasi ini sebagai i.

    last_pos = None
    while last_pos != pos:
        last_pos = pos[:]
        
        # We pick the sequence, well, in sequence starting from index 0
        for i in xrange(K):

Kemudian dari semua deret yang kita punya, selain deret yang sedang aktif dalam iterasi, kita hitung model probabilitas motif dan background. Model untuk background cukup dihitung dengan menghitung berapa kali karakter suatu karakter muncul. Sedangkan untuk model motif, kita harus menghitung berapa kali sebuah karakter muncul pada posisi ke j pada motif tersebut.

 seq_minus = sequences[:]; del seq_minus[i]
 pos_minus = pos[:]; del pos_minus[i]
 q, p = compute_model(seq_minus, pos_minus, alphabet, w)
...
def compute_model(sequences, pos, alphabet, w):
    """
    This method compute the probability model of background and word based on data in 
    the sequences.
    """
    q = {x:[1]*w for x in alphabet}
    p = {x: 1 for x in alphabet}
    
    # Count the number of character occurrence in the particular position of word
    for i in xrange(len(sequences)):
        start_pos = pos[i]        
        for j in xrange(w):
            c = sequences[i][start_pos+j]
            q[c][j] += 1
    # Normalize the count
    for c in alphabet:
        for j in xrange(w):
            q[c][j] = q[c][j] / float( len(sequences)+len(alphabet) )
    
    # Count the number of character occurrence in background position
    # which mean everywhere except in the word position
    for i in xrange(len(sequences)):
        for j in xrange(len(sequences[i])):
            if j < pos[i] or j > pos[i]+w:
                c = sequences[i][j]
                p[c] += 1
    # Normalize the count
    total = sum(p.values())
    for c in alphabet:
        p[c] = p[c] / float(total)
    
    return q, p

Setelah mendapatkan model untuk motif dan background, kita coba hitung probabilitas untuk semua posisi motif yang mungkin pada deret i dengan cara mengalikannya dengan dua model yang sudah dihitung sebelumnya. Semua karakter dimulai dari posisi awal hingga panjang motif dikalikan dengan nilai probabilitas kemunculan karakter tersebut pada posisi yang bersangkutan.

N = len(sequences[i])
qx = [1]*(N-w+1)
px = [1]*(N-w+1)
for j in xrange(N-w+1):
    for k in xrange(w):
        c = sequences[i][j+k]
        qx[j] = qx[j] * q[c][k]
        px[j] = px[j] * p[c]

Setelah itu, kita bisa menghitung rasio antara motif dan background untuk setiap kemungkinan posisi motif pada deret tersebut. Rasio yang paling besar menandakan bahwa posisi tersebut adalah posisi yang paling mungkin sebagai awal dari motif.

Aj = [x/y for (x,y) in zip(qx, px)]
norm_c = sum(Aj)
Aj = map(lambda x: x/norm_c, Aj)

Kita lakukan sampling posisi baru berdasarkan distribusi rasio. Rasio yang lebih besar tentunya memiliki peluang untuk terpilih lebih besar. Hal ini dilakukan untuk memberikan randomness dan menghindari local maxima (walaupun mungkin kurang berhasil).

pos[i] = sample(range(N-w+1), Aj)
...
def sample(alphabet, dist):
    """ This method produce a new discrete sample list by alphabet with probability
    distribution given by dist.
    The length of alphabet and dist must be same."""
    sampl = None
    cum_dist = np.cumsum(dist)
    r = rand()
    for i in xrange(len(dist)):
        if r < cum_dist[i]:
            sampl = alphabet[i]
            break
    
    return sampl

Hal ini diulang untuk semua deret, dan dilakukan hingga tidak ada lagi perubahan posisi untuk motif.

Kode lengkapnya bisa dilihat di https://github.com/mitbal/gibbs-sampler-motif-finding pada file gibbs.py

Sekarang untuk tes algoritma kita buat satu file python baru dan masukkan kode berikut

seqs = ['muhammadiqbal', 'iqbaltawakal', 'miqbalt']
k = 5

new_pos = gibbs.sampling(seqs, k)

words = [seqs[i][new_pos[i]:new_pos[i]+k] for i in xrange(len(seqs))]
print words

Jika sukses, seharusnya program di atas mengeluarkan output seperti berikut

['iqbal', 'iqbal', 'iqbal']

Namun terkadang Gibbs sampling bisa terjebak di local maxima karena inisialisasi posisi awal yang kurang baik atau pemilihan sampling posisi baru yang kurang beruntung.

['uhamm', 'iqbal', 'iqbal']

Untuk mengurangi risiko tersebut, kita bisa menjalankan gibbs sampling beberapa kali dan menghitung berapa kali output yang dihasilkan keluar. Dengan memilih output yang paling sering keluar kita bisa meningkatkan akurasi dari Gibbs sampling. Istilahnya adalah multiple chains.

result = {}
for i in xrange(20):
    new_pos = gibbs.sampling(seqs, k)
    #print new_pos
    tnp = tuple(new_pos)    
    if tnp in result:
        result[tnp] += 1
    else:
        result[tnp] = 1

max_vote = 0
max_pos = None
for key in result:
    #print key, result[key]
    if result[key] > max_vote:
        max_pos = list(key)
        max_vote = result[key]

words = [seqs[i][max_pos[i]:max_pos[i]+k] for i in xrange(len(seqs))]
print words

Selain cara diatas, module gibbs bisa dipanggil langsung dari command line. Pertama masukkan data mengenai deret dalam sebuah file dengan format baris pertama adalah panjang motif, dan kemudian setiap barisnya adalah satu deret. Contoh file “test.txt”

3
thequickdog
browndog
dogwood

Kemudian panggil program dari terminal dengan cara

 cat test.txt | python gibbs.py

atau

 python gibbs.py test.txt

Output-nya adalah posisi motif untuk setiap deret dalam bentuk list.

Semoga berguna. Salam.

Koneksi SSH ke Virtual Machine

Misalkan kita sudah membuat sebuah virtual machine di komputer/laptop kita dengan VirtualBox dan kita ingin melakukan koneksi dengan protokol SSH ke VM tersebut maka langkah-langkah yang diperlukan adalah:
1. Masuk ke menu Setting > Network, lalu aktifkan network adapter pada tab Adapter 2 dan pilih Attached to Host-only adapter
setting adapter
2. Nyalakan kembali VM. Setelah login buka dan edit file berikut /etc/network/interfaces dengan editor favorit Anda.

sudo vim /etc/network/interfaces

3. Tambahkan dua baris berikut di akhir file, silakan ganti address dengan address apapun yang Anda mau, asalkan masih 192.168

auto eth1
iface eth1 inet static
        address 192.168.56.10
        netmask 255.255.255.0

setting interfaces
4. Kemudian jalankan kode berikut

sudo ifup eth1

5. Instal openssh-server jika belum

sudo apt-get install openssh-server

Sekarang seharusnya dari komputer host, koneksi SSH ke VM dapat dilakukan dengan perintah

ssh mitbal@192.168.56.10

Jika komputer host menggunakan Windows, silakan gunakan putty atau instal cygwin dan pilih package openssh-server ketika instalasi.

Selamat mencoba, salam.

Memanggil Java dari Cygwin

untuk memanggil Java dari Cygwin, kita harus menambahkan alamat direktori Java ke environment variable PATH di cygwin.

Contoh jika Java di instal di direktori “C:\Program Files\Java\jdk1.7.0_25\bin” maka dari terminal cygwin ketikkan instruksi berikut:

export PATH=$PATH:/cygdrive/C/Program\ Files/Java/jdk1.7.0_25/bin/

Setelah itu kompilasi program Java dapat dilakukan seperti biasa.
Semoga berguna, salam.