🚀 Coba Zilliz Cloud, Milvus yang sepenuhnya terkelola, secara gratis—rasakan performa 10x lebih cepat! Coba Sekarang>>

milvus-logo
LFAI
  • Home
  • Blog
  • Pencarian Kemiripan yang Terukur dan Sangat Cepat dengan Basis Data Vektor Milvus

Pencarian Kemiripan yang Terukur dan Sangat Cepat dengan Basis Data Vektor Milvus

  • Engineering
June 21, 2022
Dipanjan Sarkar

cover image gambar sampul

Pendahuluan

Pada artikel ini, kita akan membahas beberapa aspek menarik yang berkaitan dengan database vektor dan pencarian kemiripan dalam skala besar. Di dunia yang berkembang pesat saat ini, kita melihat teknologi baru, bisnis baru, sumber data baru, dan sebagai konsekuensinya, kita harus terus menggunakan cara-cara baru untuk menyimpan, mengelola, dan memanfaatkan data ini untuk mendapatkan wawasan. Data terstruktur dan tabular telah disimpan dalam database relasional selama beberapa dekade, dan Business Intelligence berkembang pesat dalam menganalisis dan mengekstraksi wawasan dari data tersebut. Namun, dengan mempertimbangkan lanskap data saat ini, "lebih dari 80-90% data merupakan informasi yang tidak terstruktur seperti teks, video, audio, log server web, media sosial, dan banyak lagi". Organisasi telah memanfaatkan kekuatan pembelajaran mesin dan pembelajaran mendalam untuk mencoba mengekstrak wawasan dari data tersebut karena metode berbasis kueri tradisional mungkin tidak cukup atau bahkan tidak memungkinkan. Ada potensi besar yang belum dimanfaatkan untuk mengekstrak wawasan berharga dari data semacam itu dan kita baru saja memulainya!

"Karena sebagian besar data di dunia tidak terstruktur, kemampuan untuk menganalisis dan menindaklanjutinya menghadirkan peluang besar." - Mikey Shulman, Kepala ML, Kensho

Data tidak terstruktur, seperti namanya, tidak memiliki struktur implisit, seperti tabel baris dan kolom (oleh karena itu disebut data tabel atau terstruktur). Tidak seperti data terstruktur, tidak ada cara yang mudah untuk menyimpan konten data tidak terstruktur dalam database relasional. Ada tiga tantangan utama dalam memanfaatkan data tidak terstruktur untuk mendapatkan wawasan:

  • Penyimpanan: Basis data relasional biasa baik untuk menyimpan data terstruktur. Meskipun Anda dapat menggunakan database NoSQL untuk menyimpan data tersebut, akan ada biaya tambahan untuk memproses data tersebut untuk mengekstrak representasi yang tepat untuk mendukung aplikasi AI dalam skala besar.
  • Representasi: Komputer tidak memahami teks atau gambar seperti yang kita pahami. Komputer hanya memahami angka dan kita perlu menyamarkan data yang tidak terstruktur ke dalam beberapa representasi numerik yang berguna, biasanya berupa vektor atau embedding.
  • Mengajukan pertanyaan: Anda tidak dapat melakukan kueri pada data tidak terstruktur secara langsung berdasarkan pernyataan kondisional yang pasti seperti SQL untuk data terstruktur. Bayangkan, contoh sederhana Anda mencoba mencari sepatu yang serupa dengan foto sepasang sepatu favorit Anda! Anda tidak dapat menggunakan nilai piksel mentah untuk pencarian, Anda juga tidak dapat merepresentasikan fitur terstruktur seperti bentuk sepatu, ukuran, gaya, warna, dan lainnya. Sekarang bayangkan jika Anda harus melakukan hal ini untuk jutaan sepatu!

Oleh karena itu, agar komputer dapat memahami, memproses, dan merepresentasikan data yang tidak terstruktur, kita biasanya mengubahnya menjadi vektor padat, yang sering disebut penyisipan.

figure 1 Gambar 1

Ada berbagai metodologi yang secara khusus memanfaatkan deep learning, termasuk convolutional neural network (CNN) untuk data visual seperti gambar dan Transformers untuk data teks yang dapat digunakan untuk mengubah data yang tidak terstruktur menjadi embeddings. Zilliz memiliki artikel yang sangat bagus yang membahas berbagai teknik penyematan!

Sekarang, menyimpan vektor penyematan ini tidaklah cukup. Kita juga harus dapat menanyakan dan menemukan vektor-vektor yang serupa. Mengapa Anda bertanya? Mayoritas aplikasi dunia nyata didukung oleh pencarian kemiripan vektor untuk solusi berbasis AI. Ini termasuk pencarian visual (gambar) di Google, sistem rekomendasi di Netflix atau Amazon, mesin pencari teks di Google, pencarian multi-modal, de-duplikasi data, dan masih banyak lagi!

Menyimpan, mengelola, dan melakukan kueri vektor dalam skala besar bukanlah tugas yang mudah. Anda membutuhkan alat khusus untuk ini dan database vektor adalah alat yang paling efektif untuk pekerjaan ini! Pada artikel ini kita akan membahas aspek-aspek berikut:

Mari kita mulai!

Sebelumnya, kami telah menjelaskan pentingnya merepresentasikan data tidak terstruktur seperti gambar dan teks sebagai vektor, karena komputer hanya dapat memahami angka. Kami biasanya memanfaatkan model AI, atau lebih spesifiknya model deep learning untuk mengubah data yang tidak terstruktur menjadi vektor numerik yang dapat dibaca oleh mesin. Biasanya vektor-vektor ini pada dasarnya adalah daftar angka floating point yang secara kolektif mewakili item yang mendasarinya (gambar, teks, dll.).

Memahami Vektor

Mempertimbangkan bidang pemrosesan bahasa alami (NLP), kami memiliki banyak model penyematan kata seperti Word2Vec, GloVe, dan FastText yang dapat membantu merepresentasikan kata-kata sebagai vektor numerik. Dengan kemajuan dari waktu ke waktu, kita telah melihat munculnya model Transformer seperti BERT yang dapat dimanfaatkan untuk mempelajari vektor penyematan kontekstual dan representasi yang lebih baik untuk seluruh kalimat dan paragraf.

Demikian pula untuk bidang visi komputer, kami memiliki model seperti Convolutional Neural Networks (CNN) yang dapat membantu dalam mempelajari representasi dari data visual seperti gambar dan video. Dengan munculnya Transformers, kami juga memiliki Vision Transformers yang dapat bekerja lebih baik daripada CNN biasa.

figure 2 Gambar 2

Keuntungan dari vektor-vektor tersebut adalah kita dapat memanfaatkannya untuk memecahkan masalah dunia nyata seperti pencarian visual, di mana Anda biasanya mengunggah foto dan mendapatkan hasil pencarian termasuk gambar yang mirip secara visual. Google memiliki fitur ini sebagai fitur yang sangat populer di mesin pencari mereka seperti yang digambarkan dalam contoh berikut.

figure 3 Gambar 3

Aplikasi semacam itu didukung dengan vektor data dan pencarian kemiripan vektor. Jika Anda mempertimbangkan dua titik dalam ruang koordinat kartesius XY. Jarak antara dua titik dapat dihitung sebagai jarak euclidean sederhana yang digambarkan oleh persamaan berikut.

figure 4 Gambar 4

Sekarang bayangkan setiap titik data adalah vektor yang memiliki D-dimensi, Anda masih dapat menggunakan jarak euclidean atau bahkan metrik jarak lainnya seperti jarak hamming atau cosinus untuk mengetahui seberapa dekat dua titik data satu sama lain. Hal ini dapat membantu membangun gagasan kedekatan atau kemiripan yang dapat digunakan sebagai metrik yang dapat diukur untuk menemukan item yang mirip dengan item referensi menggunakan vektornya.

Pencarian kemiripan vektor, sering dikenal sebagai pencarian tetangga terdekat (NN), pada dasarnya adalah proses menghitung kemiripan berpasangan (atau jarak) antara item referensi (yang ingin kita temukan item yang mirip) dan kumpulan item yang ada (biasanya dalam database) dan mengembalikan 'k' tetangga terdekat yang merupakan 'k' teratas yang paling mirip. Komponen kunci untuk menghitung kemiripan ini adalah metrik kemiripan yang dapat berupa jarak euclidean, inner product, jarak kosinus, jarak hamming, dll. Semakin kecil jaraknya, semakin mirip vektornya.

Tantangan dengan pencarian tetangga terdekat (NN) adalah skalabilitas. Anda perlu menghitung N-jarak (dengan asumsi N item yang ada) setiap saat untuk mendapatkan item yang mirip. Ini bisa sangat lambat terutama jika Anda tidak menyimpan dan mengindeks data di suatu tempat (seperti basis data vektor!). Untuk mempercepat komputasi, kita biasanya memanfaatkan pencarian tetangga terdekat yang sering disebut pencarian ANN yang pada akhirnya menyimpan vektor ke dalam sebuah indeks. Indeks membantu menyimpan vektor-vektor ini dengan cara yang cerdas untuk memungkinkan pengambilan cepat tetangga yang 'kira-kira' mirip untuk item kueri referensi. Metodologi pengindeksan ANN yang umum meliputi:

  • Transformasi Vektor: Ini termasuk menambahkan transformasi tambahan pada vektor seperti pengurangan dimensi (misalnya PCA \ t-SNE), rotasi, dan sebagainya
  • Pengkodean Vektor: Ini termasuk menerapkan teknik berdasarkan struktur data seperti Locality Sensitive Hashing (LSH), Kuantisasi, Pohon, dll. yang dapat membantu dalam pengambilan item yang serupa dengan lebih cepat
  • Metode Pencarian yang Tidak Melelahkan: Metode ini sebagian besar digunakan untuk mencegah pencarian yang menyeluruh dan mencakup metode seperti grafik ketetanggaan, indeks terbalik, dll.

Hal ini membuktikan bahwa untuk membangun aplikasi pencarian kemiripan vektor, Anda memerlukan basis data yang dapat membantu Anda dalam menyimpan, mengindeks, dan melakukan kueri (pencarian) dalam skala besar. Masuk ke basis data vektor!

Apa yang dimaksud dengan basis data vektor?

Karena sekarang kita sudah memahami bagaimana vektor dapat digunakan untuk merepresentasikan data yang tidak terstruktur dan cara kerja pencarian vektor, kita dapat menggabungkan kedua konsep tersebut untuk membangun database vektor.

Basis data vektor adalah platform data yang dapat diskalakan untuk menyimpan, mengindeks, dan melakukan kueri di seluruh vektor penyisipan yang dihasilkan dari data tak terstruktur (gambar, teks, dll.) dengan menggunakan model pembelajaran mendalam.

Menangani vektor dalam jumlah besar untuk pencarian kemiripan (bahkan dengan indeks) bisa sangat mahal. Meskipun demikian, database vektor terbaik dan tercanggih harus memungkinkan Anda untuk memasukkan, mengindeks, dan mencari di jutaan atau miliaran vektor target, selain menentukan algoritme pengindeksan dan metrik kemiripan pilihan Anda.

Basis data vektor terutama harus memenuhi persyaratan utama berikut dengan mempertimbangkan sistem manajemen basis data yang kuat untuk digunakan di perusahaan:

  1. Terukur: Basis data vektor harus dapat mengindeks dan menjalankan pencarian perkiraan tetangga terdekat untuk miliaran vektor penyisipan
  2. Dapat diandalkan: Basis data vektor harus dapat menangani kesalahan internal tanpa kehilangan data dan dengan dampak operasional yang minimal, yaitu toleran terhadap kesalahan
  3. Cepat: Kecepatan kueri dan tulis sangat penting untuk basis data vektor. Untuk platform seperti Snapchat dan Instagram, yang dapat memiliki ratusan atau ribuan gambar baru yang diunggah per detik, kecepatan menjadi faktor yang sangat penting.

Basis data vektor tidak hanya menyimpan vektor data. Database vektor juga bertanggung jawab untuk menggunakan struktur data yang efisien untuk mengindeks vektor-vektor ini agar dapat diambil dengan cepat dan mendukung operasi CRUD (buat, baca, perbarui, dan hapus). Basis data vektor juga idealnya harus mendukung pemfilteran atribut, yaitu pemfilteran berdasarkan bidang metadata yang biasanya berupa bidang skalar. Contoh sederhananya adalah mengambil sepatu yang serupa berdasarkan vektor gambar untuk merek tertentu. Di sini, merek akan menjadi atribut yang menjadi dasar pemfilteran yang akan dilakukan.

figure 5 Gambar 5

Gambar di atas menunjukkan bagaimana Milvus, basis data vektor yang akan kita bahas sebentar lagi, menggunakan pemfilteran atribut. Milvus memperkenalkan konsep bitmask pada mekanisme pemfilteran untuk menyimpan vektor-vektor yang mirip dengan bitmask 1 berdasarkan pemenuhan filter atribut tertentu. Detail lebih lanjut tentang ini di sini.

Milvus - Basis Data Vektor Paling Canggih di Dunia

Milvus adalah platform manajemen basis data vektor sumber terbuka yang dibuat khusus untuk data vektor berskala masif dan menyederhanakan operasi pembelajaran mesin (MLOps).

figure 6 gambar 6

Zilliz, adalah organisasi di balik pembuatan Milvus, basis data vektor tercanggih di dunia, untuk mempercepat pengembangan data fabric generasi berikutnya. Milvus saat ini merupakan proyek kelulusan di LF AI & Data Foundation dan berfokus pada pengelolaan kumpulan data tak terstruktur yang sangat besar untuk penyimpanan dan pencarian. Efisiensi dan keandalan platform ini menyederhanakan proses penerapan model AI dan MLOps dalam skala besar. Milvus memiliki aplikasi yang luas yang mencakup penemuan obat, visi komputer, sistem rekomendasi, chatbot, dan banyak lagi.

Fitur-fitur Utama Milvus

Milvus dikemas dengan fitur dan kemampuan yang berguna, seperti:

  • Kecepatan pencarian yang luar biasa pada triliunan set data vektor: Latensi rata-rata pencarian dan pengambilan vektor telah diukur dalam milidetik pada satu triliun set data vektor.
  • Manajemen data tidak terstruktur yang disederhanakan: Milvus memiliki API yang kaya yang dirancang untuk alur kerja ilmu data.
  • Basis data vektor yang andal dan selalu aktif: Fitur replikasi dan failover/failback bawaan Milvus memastikan data dan aplikasi dapat selalu menjaga kelangsungan bisnis.
  • Sangat skalabel dan elastis: Skalabilitas tingkat komponen memungkinkan untuk meningkatkan dan menurunkan sesuai permintaan.
  • Pencarian hibrida: Selain vektor, Milvus mendukung tipe data seperti Boolean, String, bilangan bulat, angka floating-point, dan banyak lagi. Milvus menggabungkan pemfilteran skalar dengan pencarian kemiripan vektor yang kuat (seperti yang terlihat pada contoh kemiripan sepatu sebelumnya).
  • Struktur Lambda terpadu: Milvus menggabungkan pemrosesan stream dan batch untuk penyimpanan data untuk menyeimbangkan ketepatan waktu dan efisiensi.
  • Perjalanan Waktu: Milvus mempertahankan garis waktu untuk semua operasi penyisipan dan penghapusan data. Hal ini memungkinkan pengguna untuk menentukan stempel waktu dalam pencarian untuk mengambil tampilan data pada titik waktu tertentu.
  • Didukung oleh komunitas & diakui oleh industri: Dengan lebih dari 1.000 pengguna perusahaan, 10.000+ bintang di GitHub, dan komunitas sumber terbuka yang aktif, Anda tidak sendirian saat menggunakan Milvus. Sebagai proyek pascasarjana di bawah LF AI & Data Foundation, Milvus memiliki dukungan institusional.

Cara umum untuk membangun sistem AI yang didukung oleh pencarian kemiripan vektor adalah dengan memasangkan algoritme seperti Approximate Nearest Neighbor Search (ANNS) dengan pustaka sumber terbuka seperti:

  • Pencarian Kemiripan AI Facebook (FAISS): Kerangka kerja ini memungkinkan pencarian kemiripan yang efisien dan pengelompokan vektor yang padat. Kerangka kerja ini berisi algoritme yang mencari kumpulan vektor dengan berbagai ukuran, hingga vektor yang mungkin tidak muat dalam RAM. Ini mendukung kemampuan pengindeksan seperti multi-indeks terbalik dan kuantisasi produk
  • Spotify's Annoy (Perkiraan Tetangga Terdekat Oh Yeah): Kerangka kerja ini menggunakan proyeksi acak dan membangun sebuah pohon untuk mengaktifkan ANNS dalam skala besar untuk vektor yang padat
  • ScaNN (Scalable Nearest Neighbors) dari Google: Kerangka kerja ini melakukan pencarian kemiripan vektor yang efisien dalam skala besar. Terdiri dari implementasi, yang mencakup pemangkasan ruang pencarian dan kuantisasi untuk Maximum Inner Product Search (MIPS)

Meskipun masing-masing pustaka ini berguna dengan caranya sendiri, karena beberapa keterbatasan, kombinasi algoritma-pustaka ini tidak setara dengan sistem manajemen data vektor yang lengkap seperti Milvus. Kita akan membahas beberapa keterbatasan ini sekarang.

Keterbatasan Pendekatan yang Sudah Ada

Pendekatan yang ada saat ini yang digunakan untuk mengelola data vektor seperti yang telah dibahas pada bagian sebelumnya memiliki beberapa keterbatasan sebagai berikut:

  1. Fleksibilitas: Sistem yang ada biasanya menyimpan semua data dalam memori utama, oleh karena itu mereka tidak dapat dijalankan dalam mode terdistribusi di beberapa mesin dengan mudah dan tidak cocok untuk menangani kumpulan data yang sangat besar
  2. Penanganan data dinamis: Data sering diasumsikan statis setelah dimasukkan ke dalam sistem yang ada, sehingga mempersulit pemrosesan untuk data dinamis dan membuat pencarian yang hampir real-time menjadi tidak mungkin.
  3. Pemrosesan kueri tingkat lanjut: Sebagian besar alat tidak mendukung pemrosesan kueri tingkat lanjut (misalnya pemfilteran atribut, penelusuran hibrida, dan kueri multi-vektor), yang sangat penting untuk membangun mesin telusur kemiripan di dunia nyata yang mendukung pemfilteran tingkat lanjut.
  4. Pengoptimalan komputasi heterogen: Hanya sedikit platform yang menawarkan pengoptimalan untuk arsitektur sistem heterogen pada CPU dan GPU (tidak termasuk FAISS), yang menyebabkan hilangnya efisiensi.

Milvus berusaha mengatasi semua keterbatasan ini dan kami akan membahasnya secara rinci di bagian selanjutnya.

Keunggulan Milvus -Memahami Knowhere

Milvus mencoba mengatasi dan berhasil memecahkan keterbatasan sistem yang ada yang dibangun di atas manajemen data vektor yang tidak efisien dan algoritme pencarian kemiripan dengan cara-cara berikut:

  • Meningkatkan fleksibilitas dengan menawarkan dukungan untuk berbagai antarmuka aplikasi (termasuk SDK dalam Python, Java, Go, C++ dan RESTful API)
  • Mendukung berbagai jenis indeks vektor (misalnya, indeks berbasis kuantisasi dan indeks berbasis grafik), dan pemrosesan kueri tingkat lanjut
  • Milvus menangani data vektor dinamis menggunakan log-structured merge-tree (LSM tree), menjaga penyisipan dan penghapusan data tetap efisien dan pencarian tetap berjalan secara real time
  • Milvus juga menyediakan pengoptimalan untuk arsitektur komputasi heterogen pada CPU dan GPU modern, yang memungkinkan pengembang untuk menyesuaikan sistem untuk skenario, kumpulan data, dan lingkungan aplikasi tertentu

Knowhere, mesin eksekusi vektor dari Milvus, adalah antarmuka operasi untuk mengakses layanan di lapisan atas sistem dan pustaka pencarian kemiripan vektor seperti Faiss, Hnswlib, Annoy di lapisan bawah sistem. Selain itu, Knowhere juga bertanggung jawab atas komputasi heterogen. Knowhere mengontrol perangkat keras mana (mis. CPU atau GPU) yang akan mengeksekusi pembuatan indeks dan permintaan pencarian. Inilah bagaimana Knowhere mendapatkan namanya - mengetahui di mana harus menjalankan operasi. Lebih banyak jenis perangkat keras termasuk DPU dan TPU akan didukung dalam rilis mendatang.

figure 7 Gambar 7

Komputasi di Milvus terutama melibatkan operasi vektor dan skalar. Knowhere hanya menangani operasi-operasi pada vektor di Milvus. Gambar di atas mengilustrasikan arsitektur Knowhere di Milvus. Lapisan paling bawah adalah perangkat keras sistem. Pustaka indeks pihak ketiga berada di atas perangkat keras. Kemudian Knowhere berinteraksi dengan simpul indeks dan simpul kueri di bagian atas melalui CGO. Knowhere tidak hanya memperluas fungsi Faiss tetapi juga mengoptimalkan kinerja dan memiliki beberapa keunggulan termasuk dukungan untuk BitsetView, dukungan untuk lebih banyak metrik kemiripan, dukungan untuk set instruksi AVX512, pemilihan instruksi SIMD otomatis, dan pengoptimalan kinerja lainnya. Detailnya dapat ditemukan di sini.

Arsitektur Milvus

Gambar berikut ini menampilkan arsitektur keseluruhan platform Milvus. Milvus memisahkan aliran data dari aliran kontrol, dan dibagi menjadi empat lapisan yang independen dalam hal skalabilitas dan pemulihan bencana.

figure 8 Gambar 8

  • Lapisan akses: Lapisan akses terdiri dari sekelompok proxy tanpa kewarganegaraan dan berfungsi sebagai lapisan depan sistem dan titik akhir bagi pengguna.
  • Layanan koordinator: Layanan koordinator bertanggung jawab atas manajemen node topologi cluster, penyeimbangan beban, pembuatan stempel waktu, deklarasi data, dan manajemen data
  • Node pekerja: Node pekerja, atau eksekusi, mengeksekusi instruksi yang dikeluarkan oleh layanan koordinator dan perintah bahasa manipulasi data (DML) yang diprakarsai oleh proksi. Node pekerja di Milvus mirip dengan node data di Hadoop, atau server wilayah di HBase
  • Penyimpanan: Ini adalah landasan Milvus, yang bertanggung jawab atas persistensi data. Lapisan penyimpanan terdiri dari meta store, perantara log, dan penyimpanan objek

Lihat detail lebih lanjut tentang arsitekturnya di sini!

Melakukan pencarian gambar visual dengan Milvus - Cetak biru kasus penggunaan

Basis data vektor sumber terbuka seperti Milvus memungkinkan bisnis apa pun untuk membuat sistem pencarian gambar visual mereka sendiri dengan jumlah langkah yang minimum. Pengembang dapat menggunakan model AI yang telah dilatih sebelumnya untuk mengubah set data gambar mereka sendiri menjadi vektor, dan kemudian memanfaatkan Milvus untuk memungkinkan pencarian produk serupa berdasarkan gambar. Mari kita lihat cetak biru berikut ini tentang bagaimana merancang dan membangun sistem seperti itu.

figure 9 Gambar 9

Dalam alur kerja ini kita dapat menggunakan kerangka kerja sumber terbuka seperti towhee untuk memanfaatkan model yang sudah terlatih seperti ResNet-50 dan mengekstrak vektor dari gambar, menyimpan dan mengindeks vektor-vektor ini dengan mudah di Milvus dan juga menyimpan pemetaan ID gambar ke gambar yang sebenarnya dalam database MySQL. Setelah data diindeks, kita dapat mengunggah gambar baru dengan mudah dan melakukan pencarian gambar dalam skala besar menggunakan Milvus. Gambar berikut ini menunjukkan contoh pencarian gambar visual.

figure 10 Gambar 10

Lihatlah tutorial terperinci yang telah menjadi sumber terbuka di GitHub berkat Milvus.

Kesimpulan

Kita telah membahas cukup banyak hal dalam artikel ini. Kita mulai dengan tantangan dalam merepresentasikan data yang tidak terstruktur, memanfaatkan vektor dan pencarian kemiripan vektor dalam skala besar dengan Milvus, sebuah basis data vektor sumber terbuka. Kami membahas tentang detail tentang bagaimana Milvus terstruktur dan komponen-komponen utama yang mendukungnya serta cetak biru tentang cara memecahkan masalah dunia nyata, pencarian gambar visual dengan Milvus. Cobalah dan mulailah memecahkan masalah dunia nyata Anda sendiri dengan Milvus!

Suka dengan artikel ini? Hubungi saya untuk berdiskusi lebih lanjut atau memberikan umpan balik!

Tentang penulis

Dipanjan (DJ) Sarkar adalah Pemimpin Ilmu Data, Pakar Pengembang Google - Pembelajaran Mesin, Penulis, Konsultan, dan Penasihat AI. Hubungkan: http://bit.ly/djs_linkedin

Like the article? Spread the word

Terus Baca