Sistem rekomendasi Content-based Filtering menggunakan PHP - MySQL (part 2)

Artikel berikut ini merupakan kelanjutan dari posting sebelumnya tentang implementasi sistem rekomendasi berbasis konten (Content-based filtering Recommender System) pada PHP. Silahkan bisa menyimak terlebih dahulu artikel bagian pertama disini: Sistem rekomendasi Content-based Filtering menggunakan PHP - MySQL (part 1).

Pada project ini, saya menggunakan library Sastrawi untuk melakukan pre-processing data, dan menggunakan library Nette/Database untuk akses ke database mysql, bisa anda cek dokumentasi lengkap di sini: https://doc.nette.org/en/database/core. Dengan menggunakan composer, kita bisa tambahkan code berikut di file composer.json:

{
"require": {
"sastrawi/sastrawi": "^1.2",
"nette/database": "^3.1"
}
}


Kemudian bisa dijalankan perintah composer update pada terminal untuk mendownload beberapa library diatas ke project kita.

Pertama kita siapkan class di PHP untuk menghitung TF-IDF dan Cosine similarity seperti yang sudah dibahas di artikel bagian 1. Kali ini, saya menggunakan nama class Cbrs dan nama file disesuaikan menjadi Cbrs.php. Berikut ini kode lengkap dari class tersebut.

<?php
class Cbrs {
private $num_docs = 0;
private $corpus_terms = array();
private $doc_weight = array();
private $docs = array();
function show_docs($doc) {
$jumlah_doc = count($doc);
for($i=0; $i < $jumlah_doc; $i++) {
echo "Dokumen ke-$i : $doc[$i] <br /><br />";
}
}
# Membuat index untuk terms dari semua dokumen
function create_index($d) {
$this->docs = $d;
$this->num_docs = count($d);
foreach($d as $k => $dv){
$doc_terms = array();
$doc_terms = explode(" ", $dv);
$num_terms = count($doc_terms);
for($j=0; $j < $num_terms; $j++) {
$term = strtolower($doc_terms[$j]);
$this->corpus_terms[$term][] = array($k, $j);
}
}
}
# Menampilkan hasil dari create_index()
function show_index() {
ksort($this->corpus_terms);
foreach($this->corpus_terms AS $term => $doc_locations) {
echo "<b>$term:</b>";
echo "<br />";
foreach($doc_locations AS $doc_location){
echo "{".$doc_location[0].", ".$doc_location[1]."} ";
echo "<br />";
}
}
}
# Menghitung nilai DF
function df($term) {
$d = array();
$tr = $this->corpus_terms[$term];
foreach($tr as $t)
$d[] = $t[0];
$dx = array_unique($d);
return count($dx);
}
# Menghitung Nilai IDF
function idf() {
$ndf = [];
foreach($this->corpus_terms as $t => $terms){
$df = $this->df($t);
$ddf = $this->num_docs/$this->df($t);
$idf = round(log10($ddf), 4);
$ndf[$t][0] = $df;
$ndf[$t][1] = $idf;
}
return $ndf;
}
# Menghitung doc weight / TFIDF
function weight(){
$ndw = [];
foreach($this->docs as $k=>$d){
$dterm = explode(" ",$d);
$dx = array_count_values($dterm);
foreach($this->idf() as $t => $terms){
if(empty($dx[$t]))
$ndw[$k][$t] = 0;
else $ndw[$k][$t] = $dx[$t] * $terms[1];
}
}
$this->doc_weight = $ndw;
return $ndw;
}
# Fungsi pencarian berdasar keyword
# Pastikan keyword sudah melalui tahap pre-processing
function search($keyword){
$key = explode(" ", $keyword);
$score = [];
$i = 0;
foreach($this->doc_weight as $ndw => $w){
$score[$ndw] = 0;
foreach($w as $wg => $v){
foreach($key as $k){
if($k == $wg)
$score[$ndw] += $v;
}
}
$i++;
}
arsort($score);
return $score;
}
# Fungsi menghitung similarity ke semua dokumen
# parameter input = id dari item
public function similarity($d1){
$score = [];
foreach($this->doc_weight as $ndw => $w){
$score[$ndw] = $this->cosim($d1, $ndw);
}
arsort($score);
return $score;
}
private function cosim($d1, $d2){
$dw = $this->doc_weight;
# sum square dari 2 doc
$dw1 = $dw[$d1];
$dw2 = $dw[$d2];
$dx = 0;
$dx1 = 0;
$dx2 = 0;
foreach($this->corpus_terms as $t => $terms){
$dx += $dw1[$t] * $dw2[$t];
$dx1 += $dw1[$t] * $dw1[$t];
$dx2 += $dw2[$t] * $dw2[$t];
}
return round($dx / (sqrt($dx1) * sqrt($dx2)), 4);
}
}
view raw Cbrs.php hosted with ❤ by GitHub

Selanjutnya, kita siapkan file index.php untuk menampilkan daftar list hotel secara random dari tabel di database. Untuk tampilan HTML digunakan Bootstrap CSS supaya lebih rapi. Dan pada bagian akhir, kita buat file detail.php dimana akan menampilkan 1 hotel yang dipilih, dan kemudian akan di cari similarity/kesamaan dari sejumlah hotel dengan score paling besar berdasarkan id hotel yang dipilih tersebut.

<?php require_once('vendor/autoload.php');
require_once('Cbrs.php');
$dsn = 'mysql:host=127.0.0.1;dbname=db_hotel';
$user = 'root';
$password = '';
$database = new Nette\Database\Connection($dsn, $user, $password);
$id = $_GET['id'];
$hotel = get_hotel_detail($id, $database);
$result = $database->query('SELECT hotel_id, hotel_desc, address FROM hotel');
$data = [];
foreach($result as $row){
$data[$row->hotel_id] = pre_process($row->hotel_desc.' '.$row->address);
}
$cbrs = new Cbrs();
$cbrs->create_index($data);
$cbrs->idf();
$w = $cbrs->weight();
$r = $cbrs->similarity($id);
$n = 8;
function pre_process($str){
$stemmerFactory = new \Sastrawi\Stemmer\StemmerFactory();
$stemmer = $stemmerFactory->createStemmer();
$stopWordRemoverFactory = new \Sastrawi\StopWordRemover\StopWordRemoverFactory();
$stopword = $stopWordRemoverFactory->createStopWordRemover();
$str = strtolower($str);
$str = $stemmer->stem($str);
$str = $stopword->remove($str);
return $str;
}
function get_hotel_detail($id, $db){
$rs = $db->fetch('SELECT * FROM hotel Where hotel_id = '.$id);
return $rs;
}
?>
<!doctype html>
<html lang="en">
<head>
<title>Content-based Filtering</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.4.1/dist/css/bootstrap.min.css" integrity="sha384-HSMxcRTRxnN+Bdg0JdbxYKrThecOKuH5zCYotlSAcp1+c8xmyTe9GYg1l9a69psu" crossorigin="anonymous">
</head>
<body>
<div class="container theme-showcase">
<div class="jumbotron">
<h1>Daftar Hotel di Surabaya</h1>
<p>Contoh implementasi Sistem rekomendasi berbasis kontent menggunakan metode TF-IDF dan Cosine Similarity</p>
</div>
<div class="row">
<div class="col-md-2">
<img src="https://via.placeholder.com/150" />
</div>
<div class="col-md-10">
<h2><span class="label label-primary"><?php echo $hotel->hotel_name?></span></h2>
<p><strong>Address:</strong> <?php echo $hotel->address?></p>
<p><strong>Description:</strong> <?php echo $hotel->hotel_desc?></p>
<p><strong>Price/Night:</strong> Rp <?php echo number_format($hotel->price_per_night)?></p>
</div>
</div>
<div class="row">
<div class="col-md-12">
<h3>Rekomendasi Hotel yang sesuai</h3>
<ol>
<?php $i=0;?>
<?php foreach($r as $k => $row):?>
<?php if($i==$n) break;?>
<?php if($row==1) continue;?>
<?php $h = get_hotel_detail($k, $database);?>
<li><a href="detail.php?id=<?php echo $h->hotel_id ?>">
<?php echo $h->hotel_name ?></a> (<?php echo $row?>)
</li>
<?php $i++ ?>
<?php endforeach ?>
</ol>
</div>
</div>
</div>
</body>
</html>
view raw detiail.php hosted with ❤ by GitHub
<?php require_once('vendor/autoload.php');
require_once('Cbrs.php');
$dsn = 'mysql:host=127.0.0.1;dbname=db_hotel';
$user = 'root';
$password = '';
$database = new Nette\Database\Connection($dsn, $user, $password);
$result = $database->query('SELECT hotel_id, hotel_name, address, price_per_night FROM hotel order by rand() limit 0,10');
?>
<!doctype html>
<html lang="en">
<head>
<title>Content-based Filtering</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.4.1/dist/css/bootstrap.min.css" integrity="sha384-HSMxcRTRxnN+Bdg0JdbxYKrThecOKuH5zCYotlSAcp1+c8xmyTe9GYg1l9a69psu" crossorigin="anonymous">
</head>
<body>
<div class="container theme-showcase">
<div class="jumbotron">
<h1>Daftar Hotel di Surabaya</h1>
<p>Contoh implementasi Sistem rekomendasi berbasis kontent menggunakan metode TF-IDF dan Cosine Similarity</p>
</div>
<div>
<table class="table table-striped">
<thead>
<tr>
<th>#</th>
<th>Hotel Name</th>
<th>Hotel Address</th>
<th>Price/Night</th>
</tr>
</thead>
<tbody>
<?php $no=1?>
<?php foreach($result as $row):?>
<tr>
<td><?php echo $no++?></td>
<td><a href="detail.php?id=<?php echo $row->hotel_id ?>">
<?php echo $row->hotel_name ?></a>
</td>
<td><?php echo $row->address ?></td>
<td><?php echo 'Rp '.number_format($row->price_per_night) ?></td>
</tr>
<?php endforeach ?>
</tbody>
</table>
</div>
</div>
</body>
</html>
view raw index.php hosted with ❤ by GitHub

Pada kode diatas, terdapat function pre_process() dan get_hotel_detail(), dimana kegunaan fungsi pertama untuk melakukan pembersihan data sebelum masuk ke class perhitungan TF-IDF dan cosine similarity di class Cbrs dan fungsi kedua untuk menampilkan detail hotel berdasarkan parameter id. Variabel $n digunakan untuk menyimpan jumlah item rekomendasi yang akan ditampilkan (top-n). Dan pada bagian perulangan variabel $r ditambahkan keyword continue; untuk menghilangkan item kunci, dimana nilainya = 1 dikarenakan menghitung similarity dari item yang sama. Selain itu, digunakan juga keyword break; untuk keluar dari perulangan jika jumlah n rekomendasi sesuai dengan variabel $n.

Tampilan antar muka detail hotel dan rekomendasi 8 item (beserta score similarity)

Jika anda membutuhkan kode lengkap dari project diatas beserta file database sql, silahkan tuliskan alamat email di bagian komentar. InshaAllah akan segera saya kirimkan ke email anda.


Email ThisBlogThis!Share to TwitterShare to Facebook

Posting Komentar

Lebih baru Lebih lama