Membuat Fitur “Produk Serupa” Dengan PredictionIO


PredictionIO (https://prediction.io) adalah sebuah engine yang dirancang untuk mudah dipakai dalam menerapkan machine learning. Lalu apa manfaatnya? Produk ini tidak hanya berguna untuk peneliti, tetapi juga bisa diterapkan langsung pada situs e-commerce. Salah satunya adalah PredictionIO bisa digunakan untuk menghasilkan daftar “produk serupa” berdasarkan perilaku dari pengunjung lain. Tanpa machine learning, pembuat situs biasanya menampilkan produk yang acak berdasarkan tag tertentu. Dengan menghasilkan daftar “produk serupa” secara pintar, pengguna akan memperoleh saran yang lebih akurat sehingga penjualan bisa meningkat, terutama bagi situs e-commerce yang memiliki banyak produk bervariasi.

Sebagai latihan, saya akan menjalankan engine PredictionIO pada sebuah server Linux yang terpisah dari server web. Salah satu kelebihan PredictionIO adalah masing-masing algoritma dikelompokkan dalam apa yang disebut dengan engine template. Saya dapat menemukan daftar engine template siap pakai di https://templates.prediction.io. Sesungguhnya sebuah engine template bukan hanya mengandung algoritma, tetapi komponen lengkap yang disebut DASE (Data, Algorithm, Serving, Evaluator). Developer juga bisa memodifikasi kode program engine template (dalam bahasa Scala) sesuai dengan kebutuhan.

Karena ingin memunculkan “produk serupa”, saya akan menggunakan template PredictionIO/template-scala-parallel-complementarypurchase yang dapat dijumpai di https://templates.prediction.io/PredictionIO/template-scala-parallel-complementarypurchase. Berdasarkan informasi dari dokumentasi, algoritma yang dipakai oleh engine template ini mengikuti konsep association rule learning (https://en.wikipedia.org/wiki/Association_rule_learning).

Sebagai langkah awal, saya memberikan perintah berikut ini untuk men-download kode program engine template:

$ pio template get PredictionIO/template-scala-parallel-complementarypurchase latihanEngine
Please enter author's name: latihan
Please enter the template's Scala package name (e.g. com.mycompany): com.latihan
Please enter author's e-mail address: 
Author's name:         latihan
Author's e-mail:       
Author's organization: com.latihan
Would you like to be informed about new bug fixes and security updates of this template? (Y/n) n
Retrieving PredictionIO/template-scala-parallel-complementarypurchase
There are 5 tags
Using tag v0.3.1
Going to download https://github.com/PredictionIO/template-scala-parallel-complementarypurchase/archive/v0.3.1.zip
Redirecting to https://codeload.github.com/PredictionIO/template-scala-parallel-complementarypurchase/zip/v0.3.1
Replacing org.template.complementarypurchase with com.latihan...
Processing latihanEngine/build.sbt...
Processing latihanEngine/engine.json...
Processing latihanEngine/src/main/scala/Algorithm.scala...
Processing latihanEngine/src/main/scala/DataSource.scala...
Processing latihanEngine/src/main/scala/Engine.scala...
Processing latihanEngine/src/main/scala/Preparator.scala...
Processing latihanEngine/src/main/scala/Serving.scala...
Engine template PredictionIO/template-scala-parallel-complementarypurchase is now ready at latihanEngine

Perintah di atas akan menciptakan sebuah folder bernama latihanEngine yang berisi kode program yang bisa saya modifikasi sesuai keperluan. Karena akan bekerja pada folder ini, maka saya perlu pindah ke folder ini dengan memberikan perintah:

$ cd latihanEngine

Setelah itu, saya siap untuk menjalankan PredictionIO dengan memberikan perintah seperti berikut ini:

$ pio-start-all

Saya bisa memerika apakah semua komponen dijalankan dengan baik dengan menggunakan perintah seperti berikut ini:

$ pio status
PredictionIO
  Installed at: /home/snake/PredictionIO
  Version: 0.9.2

Apache Spark
  Installed at: /home/snake/PredictionIO/vendors/spark-1.3.0
  Version: 1.3.0 (meets minimum requirement of 1.3.0)

Storage Backend Connections
  Verifying Meta Data Backend
  Verifying Model Data Backend
  Verifying Event Data Backend
  Test write Event Store (App Id 0)
[INFO] [HBLEvents] The table predictionio_eventdata:events_0 doesn't exist yet. Creating now...
[INFO] [HBLEvents] Removing table predictionio_eventdata:events_0...

(sleeping 5 seconds for all messages to show up...)
Your system is all ready to go.

Perintah di atas memerika apakah komponen yang dipakai oleh PredictionIO semuanya sudah siap dipakai. Terlihat bahwa PredictionIO memakai Apache Spark untuk mengerjakan algoritma secara paralel dan scalable. Selain itu, data yang dikumpulkan akan disimpan ke dalam Apache HBase. Ini adalah sebuah database No-SQL yang dirancang untuk dipakai pada HDFS (Hadoop File System).

Langkah awal pada machine learning adalah mengumpulkan data sebanyak mungkin untuk keperluan training. PredictionIO akan membuat sebuah web service server berbasis REST yang dapat dipakai oleh aplikasi web untuk memberikan data. Untuk itu, saya perlu memperoleh sebuah access key terlebih dahulu dengan memberikan perintah:

$ pio app new LatihanWeb
[INFO] [HBLEvents] The table predictionio_eventdata:events_2 doesn't exist yet. Creating now...
[INFO] [App$] Initialized Event Store for this app ID: 2.
[INFO] [App$] Created new app:
[INFO] [App$]       Name: LatihanWeb
[INFO] [App$]         ID: 2
[INFO] [App$] Access Key: A7iLxxkf7RGBpiTtxMGu8zi4EZ0Kfcox05HTwYDCteCn5weqtdnoGyEaRCRYX2CM

Bagian yang paling penting dari output di atas adalah access key yang perlu saya berikan saat memanggil event server yang diciptakan oleh PredictionIO. Untuk memastikan event server berjalan dengan baik, saya bisa mengakses URL seperti http://192.168.10.100:7070 dari browser dimana 192.168.10.100 adalah IP server yang menjalankan PredictionIO. Saya akan memperoleh hasil seperti {"status":"alive"}. Bila memakai terminal, saya juga bisa menggunakan perintah curl seperti berikut ini:

$ curl 192.168.10.100:7070
{"status":"alive"}

Sekarang, saya siap untuk mengirimkan event kepada event server. Sebagai latihan, saya akan memakai access logs untuk web distribution dari Amazon CloudFront milik sebuah situs. Saya hanya tertarik pada kolom ke-12 (cs-uri-query) dimana saya mengambil nilai parameter item_id yang mewakili item yang dilihat pengguna dan visitor_id yang mewakili pengenal unik untuk pengguna tersebut. Engine complementary purchase yang saya pakai secara default mendukung event buy yang membutuhkan parameter berupa user dan item. Agar sederhana, saya tidak akan melakukan perubahan kode program dan menganggap event buy tersebut sama seperti view. Saya kemudian membuat sebuah script Perl untuk membaca dan mengakses event server seperti berikut ini:

#!/usr/bin/perl

$LOGFILE = "access.log";
open(LOGFILE) or die("Tidak dapat membaca $LOGFILE");
while () {
  next if 1..2;
  $uri = (split(' ', $_))[11];
  if ($uri =~ m/item_id%253D(\w+)%2526/) {
    $itemId = $1;
  } else {
    print "Tidak dapat menemukan item_id di $_\n";
  }
  if ($uri =~ m/visitor_id%253D([\w-]+)&/) {
    $browserId = $1;
  } else {
    print "Tidak dapat menemukan visitor_id di $_\n";
  }
  ($postEvent = <<"CURL") =~ s/\n+//gm;
curl -i -X POST http://192.168.10.100:7070/events.json?accessKey=A7iLxxkf7RGBpiTtxMGu8zi4EZ0Kfcox05HTwYDCteCn5weqtdnoGyEaRCRYX2CM \
-H "Content-Type: application/json" \
-d '{
  "event": "buy",
  "entityType": "user",
  "entityId": "$browserId",
  "targetEntityType": "item",
  "targetEntityId": "$itemId"
}'
CURL
  system($postEvent);
}

Script di atas pada dasarnya akan menggunakan curl untuk menambahkan event pada event server. Seluruh event yang terkumpul akan diletakkan pada database Apache HBase. Untuk melihat apakah event sudah tersimpan dengan baik, saya bisa membuka URL seperti http://192.168.10.100:7070/events.json?accessKey=A7iLxxkf7RGBpiTtxMGu8zi4EZ0Kfcox05HTwYDCteCn5weqtdnoGyEaRCRYX2CM melalui browser.

Bila selama percobaan, event yang diberikan tidak benar atau perlu dihapus, saya bisa menggunakan perintah seperti berikut ini:

$ pio app data-delete LatihanWeb
[INFO] [App$] Data of the following app (default channel only) will be deleted. Are you sure?
[INFO] [App$]     App Name: LatihanWeb
[INFO] [App$]       App ID: 2
[INFO] [App$]  Description: None
Enter 'YES' to proceed: YES
[INFO] [HBLEvents] Removing table predictionio_eventdata:events_2...
[INFO] [App$] Removed Event Store for this app ID: 2
[INFO] [HBLEvents] The table predictionio_eventdata:events_2 doesn't exist yet. Creating now...
[INFO] [App$] Initialized Event Store for this app ID: 2.
[INFO] [App$] Done.

Setelah event terkumpul, sekarang saatnya untuk menjalankan engine. Tapi sebelumnya, saya perlu membuka file engine.json dan menguna nilai appName menjadi latihanWeb (sesuai dengan nama yang sama berikan saat mengerjakan pio app new). Setelah itu, saya memberikan perintah berikut ini:

$ pio build --verbose
...
[INFO] [Console$] Your engine is ready for training.

Perintah di atas akan men-download dependency Ivy yang dibutuhkan (dengan menggunakan Scala sbt, sejenis Gradle di Groovy) dan men-compile kode program yang sebelumnya dihasilkan oleh engine template. Setelah proses building selesai, langkah berikutnya adalah melakukan training untuk predictive model dengan memberikan perintah ini:

$ pio train
...
[INFO] [CoreWorkflow$] Training completed successfully.

Perintah ini akan menjalankan proses training di Apache Spark. Bila terdapat kesalahan yang berkaitan dengan java.lang.StackOverflowError, maka stack size bisa ditingkatkan menjadi lebih besar, misalnya 16 MB dengan membuat environment variable bernama SPARK_JAVA_OPTS yang memiliki isi seperti -Xss16M.

Setelah proses training selesai, saya bisa men-deploy engine agar hasilnya dapat diakses melalui REST. Sebagai contoh, saya memberikan perintah berikut ini:

$ pio deploy
...
[INFO] [MasterActor] Bind successful. Ready to serve.

Untuk memastikan engine sudah berjalan dengan baik, saya bisa mencoba mengakses URL seperti http://192.168.10.100:8000. Bila hasilnya muncul seperti harapan, maka saya siap memodifikasi aplikasi web untuk mengakses engine ini.

Untuk mendapatkan rekomendasi produk sejenis, server web (atau server lain yang membutuhkan) perlu mengirimkan JSON yang mengandung sebuah id item di key "items" dan jumlah rekomendasi yang dibutuhkan di key "num". Sebagai contoh, saya bisa mengakses engine seperti berikut ini:

$ curl -H "Content-Type: application/json" -d '{"items": ["xxx"], "num": 3}' http://192.168.10.100:8000/queries.json
{
  "rules": [
    {
     "cond": ["xxx"], 
     "itemScores":[
        {"item":"hasil1", "support":3.92541707556427E-4, "confidence":0.166666,"lift":424.583333},
        {"item":"hasil2","support":3.925417075564279E-4,"confidence":0.166666,"lift":424.58333333},
        {"item":"hasil3", "support":3.925417075564279E-4,"confidence":0.1666666,"lift":424.58333333}]
    }
  ]
}

Sebagai contoh, pada hasil yang saya peroleh, 3 rekomendasi untuk sebuah produk iPhone 5s adalah produk ZenFone 2, iPhone 5s dari penjual berbeda, dan iPhone 6. Hasil ini cukup masuk akal secara sekilas. Yang perlu diperhatikan adalah hasil ini tidak menyertakan riwayat produk yang sudah pernah dilihat oleh user tersebut, melainkan rekomendasi produk serupa berdasarkan pola kunjungan pengguna lain.

Bila mencoba dengan data dengan variasi kunjungan yang terbatas, misalnya snapshot untuk beberapa menit dimana masing-masing item barang hanya dikunjungi satu dua kali oleh pengguna, saya bisa mengubah parameter algoritma di engine.json menjadi seperti berikut ini:

"algorithms": [
    {
      "name": "algo",
      "params": {
        "basketWindow" : 300,
        "maxRuleLength" : 2,
        "minSupport": 0,
        "minConfidence": 0,
        "minLift" : 0,
        "minBasketSize" : 2,
        "maxNumRulesPerCond": 5
      }
    }
]

Mengubah nilai minSupport, minConfidence dan minLift menjadi 0 akan mengurangi kualitas hasil yang diperoleh. Sebagai contoh, nilai default untuk minSupport adalah 0.1 yang berarti item harus muncul minimal 10% untuk seluruh transaksi agar ia disertakan pada hasil. Oleh sebab itu, pengaturan seperti ini sebaiknya tidak dipakai pada kasus nyata.

Perihal Solid Snake
I'm nothing...

Apa komentar Anda?

Please log in using one of these methods to post your comment:

Logo WordPress.com

You are commenting using your WordPress.com account. Logout / Ubah )

Gambar Twitter

You are commenting using your Twitter account. Logout / Ubah )

Foto Facebook

You are commenting using your Facebook account. Logout / Ubah )

Foto Google+

You are commenting using your Google+ account. Logout / Ubah )

Connecting to %s

%d blogger menyukai ini: