Kemiripan kosinus mengukur kemiripan dua vektor melalui sudut di antara keduanya, tanpa memperhatikan panjang masing-masing vektor. Topik ini membahas alasan sudut lebih tepat daripada dot product mentah sebagai ukuran kemiripan, perhitungannya dengan NumPy, dan penggunaannya pada pencarian dokumen serta pencocokan embedding. Setelah topik ini dibaca, kemiripan dua vektor dapat dihitung dan dibandingkan dengan benar.
Sudut sebagai ukuran kemiripan#
Membagi dot product dua vektor dengan hasil kali normanya menghasilkan ukuran kemiripan yang hanya bergantung pada arah, yaitu
Karena kosinus selalu berada di antara -1 dan 1, nilainya dapat dibaca langsung sebagai skala kemiripan arah. Gambar berikut menampilkan dua vektor yang membentuk sudut 45 derajat. Pada sudut itu, , suatu nilai di antara searah penuh dan tegak lurus.
Tabel berikut merangkum pembacaan nilai kemiripannya.
| Nilai kemiripan | Makna arah |
|---|---|
| 1 | Kedua vektor searah penuh |
| Mendekati 1 | Kedua vektor cenderung searah |
| 0 | Kedua vektor tegak lurus, tidak ada keselarasan |
| -1 | Kedua vektor berlawanan arah |
Pembagian oleh norma inilah yang membedakan ukuran ini dari dot product biasa. Dot product mentah membesar ketika salah satu vektornya dipanjangkan, padahal arahnya tidak berubah. Ukuran kosinus menghilangkan pengaruh panjang tersebut sehingga hanya sudut yang menentukan nilainya.
Ukuran jarak Euclidean kurang tepat untuk membandingkan isi dokumen karena dokumen panjang cenderung berjarak jauh dari dokumen pendek. Kemiripan kosinus mengatasi hal tersebut dengan membuang informasi panjang sejak awal. Dua versi tulisan yang sama, yaitu ringkas dan lengkap, berjarak jauh secara Euclidean tetapi bersudut nol dan kemiripannya satu. Perbandingan berbasis sudut juga tidak memerlukan penyesuaian apa pun ketika panjang vektornya berbeda jauh.
Perhitungan dengan NumPy#
Berkas kemiripan_kosinus.py berikut menghitung kemiripan kosinus dari tiga vektor frekuensi kata. Vektor tersebut berasal dari catatan sensor dengan urutan komponen suhu, angin, dan hujan. Vektor a dan b sama-sama menonjolkan suhu dan angin, sedangkan vektor c hanya mencatat hujan.
import numpy as np
def kemiripan_kosinus(x, y):
pembilang = np.dot(x, y)
penyebut = np.linalg.norm(x) * np.linalg.norm(y)
return pembilang / penyebut
# frekuensi kata dari catatan sensor, urutan kata: (suhu, angin, hujan)
sensor_a = np.array([3.0, 2.0, 0.0])
sensor_b = np.array([2.0, 3.0, 0.0])
sensor_c = np.array([0.0, 0.0, 4.0])
print("kemiripan(a, b) =", round(kemiripan_kosinus(sensor_a, sensor_b), 4))
print("kemiripan(a, c) =", round(kemiripan_kosinus(sensor_a, sensor_c), 4))
# kemiripan kosinus kebal terhadap panjang vektor
print("kemiripan(a, 5b) =", round(kemiripan_kosinus(sensor_a, 5 * sensor_b), 4))
# pembanding: dot product biasa ikut berubah oleh panjang vektor
print("dot(a, b) =", np.dot(sensor_a, sensor_b))
print("dot(a, 5b) =", np.dot(sensor_a, 5 * sensor_b))kemiripan(a, b) = 0.9231
kemiripan(a, c) = 0.0
kemiripan(a, 5b) = 0.9231
dot(a, b) = 12.0
dot(a, 5b) = 60.0Nilai kemiripan a dan b yang tinggi, yaitu 0,9231, sesuai dengan isi kedua catatan yang sama-sama membahas suhu dan angin. Kemiripan a dengan c bernilai nol karena kedua vektornya saling tegak lurus, yaitu tidak ada satu pun kata yang muncul pada kedua catatan tersebut. Baris ketiga menunjukkan ketahanan ukuran ini terhadap panjang, sebab kemiripan a dengan 5b tetap 0,9231. Dot product mentah pada dua baris terakhir justru naik lima kali lipat dari 12 menjadi 60 ketika b dipanjangkan, dan di situlah letak kekurangannya sebagai ukuran kemiripan. Fungsi kemiripan_kosinus pada skrip memuat rumusnya apa adanya, yaitu np.dot pada pembilang dan hasil kali np.linalg.norm pada penyebutnya.
Demo interaktif#
Demo berikut mengatur sudut antara vektor a dan vektor b dengan penggeser. Kedua vektor digambar berpanjang satu pada lingkaran satuan, sehingga perubahan sudut tidak disertai perubahan panjang. Geser penggeser dari 0 hingga 180 derajat, kemudian amati nilai kosinusnya menurun dari 1 hingga -1.
Kode sumber demo
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="utf-8">
<title>Demo sudut dan kemiripan kosinus</title>
<style>
body { font-family: sans-serif; margin: 12px; }
canvas { border: 1px solid #ccc; display: block; margin-bottom: 8px; background: #fff; }
p { margin: 6px 0; }
label { font-size: 14px; }
input { vertical-align: middle; }
.nilai { font-weight: bold; }
</style>
</head>
<body>
<canvas id="kanvas" width="440" height="260"></canvas>
<p>
<label>Sudut antara a dan b (derajat):
<input type="range" id="sudut" min="0" max="180" value="45">
<span class="nilai" id="tampilan-sudut">45</span>
</label>
</p>
<p class="nilai" id="kesimpulan"></p>
<script>
const kvs = document.getElementById("kanvas").getContext("2d");
const lebar = 440, tinggi = 260;
const cx = 110, cy = 130; // titik nol agak ke kiri agar label lega
const r = 75; // jari-jari lingkaran satuan
function panah(x1, y1, warna, label) {
// vektor selalu berpangkal di titik nol dan berpanjang satu
const X1 = cx + x1 * r, Y1 = cy - y1 * r;
kvs.strokeStyle = warna;
kvs.fillStyle = warna;
kvs.lineWidth = 2;
kvs.beginPath();
kvs.moveTo(cx, cy);
kvs.lineTo(X1, Y1);
kvs.stroke();
const sudut = Math.atan2(Y1 - cy, X1 - cx);
kvs.beginPath();
kvs.moveTo(X1, Y1);
kvs.lineTo(X1 - 9 * Math.cos(sudut - 0.4), Y1 - 9 * Math.sin(sudut - 0.4));
kvs.lineTo(X1 - 9 * Math.cos(sudut + 0.4), Y1 - 9 * Math.sin(sudut + 0.4));
kvs.closePath();
kvs.fill();
kvs.fillText(label, X1 + 6, Y1 - 6);
}
function gambar() {
const derajat = Number(document.getElementById("sudut").value);
const theta = derajat * Math.PI / 180;
const a = [Math.cos(theta), Math.sin(theta)];
kvs.clearRect(0, 0, lebar, tinggi);
// sumbu koordinat
kvs.strokeStyle = "#999";
kvs.lineWidth = 1;
kvs.beginPath(); kvs.moveTo(0, cy); kvs.lineTo(lebar, cy); kvs.stroke();
kvs.beginPath(); kvs.moveTo(cx, 0); kvs.lineTo(cx, tinggi); kvs.stroke();
// lingkaran satuan
kvs.strokeStyle = "#ccc";
kvs.beginPath();
kvs.arc(cx, cy, r, 0, 2 * Math.PI);
kvs.stroke();
// busur sudut antara a dan b
kvs.strokeStyle = "#555";
kvs.beginPath();
kvs.arc(cx, cy, 26, -theta, 0);
kvs.stroke();
panah(1, 0, "#1a56b0", "b");
panah(a[0], a[1], "#d97706", "a");
const kos = Math.cos(theta);
let sifat = "berlawanan arah";
if (kos > 0.9) {
sifat = "hampir searah";
} else if (kos > 0.3) {
sifat = "searis";
} else if (kos >= -0.3) {
sifat = "hampir tegak lurus";
} else if (kos >= -0.9) {
sifat = "cenderung berlawanan";
}
document.getElementById("tampilan-sudut").textContent = String(derajat);
document.getElementById("kesimpulan").textContent =
"kosinus = " + kos.toFixed(2).replace(".", ",") + " → " + sifat;
}
document.getElementById("sudut").addEventListener("input", gambar);
gambar();
</script>
</body>
</html>Di mana hal ini ditemui dalam kode#
Pencarian dokumen menggunakan kemiripan kosinus sebagai ukuran peringkat utama. Setiap dokumen direpresentasikan sebagai vektor frekuensi kata, kueri pencarian diubah menjadi vektor serupa, lalu dokumen diurutkan menurut kemiripan kosinusnya dengan kueri. Panjang dokumen tidak menggeser peringkat karena ukurannya hanya melihat arah, dan dokumen panjang pun berpeluang menyamai dokumen pendek yang bertema sama. Mesin pencari internal pada aplikasi catatan atau dokumentasi perangkat lunak kerap menggunakan skema sederhana ini sebagai langkah pertamanya.
Embedding modern melanjutkan ide yang sama. Kata, kalimat, dan gambar dipetakan menjadi vektor berdimensi tinggi, kemudian pencarian tetangga terdekat membandingkan vektor-vektor tersebut dengan kemiripan kosinus. Ukuran ini dipilih karena panjang vektor embedding dalam praktiknya tidak membawa makna, sedangkan arahnya yang memuat makna. Pencocokan preferensi pada sistem rekomendasi juga menggunakan ukuran ini, yaitu membandingkan vektor riwayat pilihan dengan vektor karakteristik setiap item, dan item dengan kemiripan tertinggi ditempatkan paling depan.
Ringkasan#
- Kemiripan kosinus adalah dot product dua vektor dibagi hasil kali normanya, sehingga panjang vektor tidak memengaruhi nilainya.
- Nilainya berada di antara -1 dan 1, dengan 1 untuk searah, 0 untuk tegak lurus, dan -1 untuk berlawanan arah.
- Penggandaan salah satu vektor dengan skalar positif tidak mengubah kemiripannya, sedangkan dot product mentah ikut berubah.
- Pada vektor satuan, kemiripan kosinus sama dengan dot product biasa.
Referensi#
- numpy.dot — NumPy
— Dokumentasi resmi
np.dotyang digunakan pada pembilang rumus kemiripan. - Mathematics for Machine Learning — Cambridge University Press — Buku terbuka yang membahas hasil kali dalam dan sudut antarvektor sebagai dasar pengukuran kemiripan.