![](https://boute.s3.amazonaws.com/263-card-25_8.jpg)
سال ۲۰۰۱ شرکت اپل با معرفی سرویس آیتیونز انقلاب بزرگی در صنعت موسیقی ایجاد کرد و تنها پس از چند سال این صنعت را کاملا به سمت دیجیتالیشدن کشاند. اما این انقلاب هم تغییر بنیادینی در رفتار کاربر در آهنگ گوشکردن ایجاد نکرد. نهایتا کاربر برای شنیدن موسیقی باز هم مجبور بود حتما آنرا به طور کامل خریداری کند. اما معرفی سرویسهای استریم موسیقی مانند اسپاتیفای در سال ۲۰۰۸ باعث شد تغییر کاملا قابل توجهی در نحوهی آهنگشنیدن و انتخاب آهنگ در کاربران ایجاد کرد.
![تصویر ۱-۰: پیشنهاد آهنگ در اسپاتیفای](https://boute.s3.amazonaws.com/263-spotify.jpg)
در این سرویسها برای شنیدن آهنگ کافی است رو اسم آن کلیک کرد، آهنگ بدون نیاز به دانلود کامل شروع به پخششدن میکند. این شیوه باعث شد کاربر خود را مقابل میلیونها آهنگ ببیند. اینجا بود که میزان گوش دادن آهنگ برای خوانندهها بسیار تعیینکننده شد. برای همین این سرویسها برای بقای خود مجبور شدند سلیقهی کاربر را بفهمند و آهنگ مناسب آن را پیشنهاد دهند و به او کمک کنند تا آهنگ مورد پسندش را پیدا کند.
با ورود سایر غولهای اینترنتی مانند گوگل و اپل به استریم موسیقی، پیشنهاد آهنگ و تبعا یادگیری ماشینی در این صنعت توجهات بسیاری را به خود جلب کرد به طوری که امروزه سرمایههای میلیون دلاری روانهی آن میشود.
# مقدمه
با گسترش این سرویسها، حالا مشکل طبقهبندی و مدیریت میلیونها آهنگ است. تکنیکهای به دستآوردن اطلاعات از موسیقی[^MIR] از مدتها قبل توسعه پیدا کردند تا به ما در مواردی مانند تشخیص ژانر آهنگ، خواننده و نوع ساز کمک کنند. با این حال یک پیشنهاددهندهی آهنگ خوب باید بتواند سلیقهی کاربر را به طور خودکار تشخیص دهد و به او در پیداکردن و فیلتر آهنگها کمک کند.
در طول این سالها روشهایی برای پیشنهاد آهنگ توسعه داده شدند که پیشنهاد آهنگ را صرفا با استفاده از تاریخچهی امتیازدهی کاربران و پیدا کردن تاریخچهی مشابه در کاربران انجام دادهاند و موفقیتهای بسیاری هم کسب کردهاند.
اما نکته اینجاست که موسیقی ذاتا نه تنها انتقالدهندهی احساست است بلکه حال و هوا (اصلاحا مود) کاربر را هم منتقل میکند؛ سلیقهی موسیقی از فردی به فردی دیگر کاملا متفاوت است. از همینرو روشهای گذشته نمیتواند تمامی نیازها را برطرف کنند.
## اجزای تشکلیلدهندهی یک پیشنهاددهندهی آهنگ
عموما یک سیستم پیشنهاد دهنده از ۳ قسمت تشکیل میشود. کاربر، آهنگ، مچینگ بین آهنگ و کاربر؛[1]
**۱- ساخت مدلی از کاربر:**
مدل سازی از کاربر نقش بسیار کلیدی در این نوع سیستمها ایفا میکنند. در واقع ما عاملهایی که باعث تفاوت میشوند را مدل میکنیم. به طور مثال منطقهی مختلف جغرافیای میتواند سلیقهی موسیقی متفاوتی را در بر داشته باشد. همچنین مواردی مانند سن، جنیست و علایق هم در انتخاب کاربر تاثیر گذارند.
حتی تحقیقات هم نشان دادهاند که معمولا افراد اجتماعی و برونگرا موسیقیهای پر انرژیتر و ریتمیک را بیشتر میپسند بنابراین مدلسازی از کاربر نقش حیاتی را در سیستمما ایفا میکند.
| نوع داده | مثال |
|:---------|:------:|
|شخصی| سن، جنسیت و ...|
|حغرافیایی| مکان، شهر، کشور|
|روانی| ثابت: علایق، سبکزندگی، شخصیت و ... متغیر: مود، نظرات و ...|
سایر اطلاعات مانند تازیخچهی گوشدادن و نظرات روی آهنگها هم شکلدهندهی بخش عظیمی مدل کاربر هستند که کاملا باعث میشود سلقیهی او را بهتر بشناسیم.
**۲- ساخت مدلی از آهنگ:**
دومین بخش از سیستمهای پیشنهاد دهندهی آهنگ، خود آهنگ است. سه نوع اطلاعات را میتوان از موسیقی دریافت کرد.
+ اطلاعات مربوط به خواننده، ژانر، زمان تولید و ...
+ اطلاعات دریافتی از متن آن
+ اطلاعات آکوستیک: اطلاعات قابل دریافت با استفاده از پردازش سیگنال آهنگ
در بین سیستمهای پیشنهاد دهنده یکی از سختترین قسمتهای توسعهی آن دریافت اطلاعات از سیگنال آهنگ است. چرا که تشخیص و پردازش آن برای کامپیوتر بسیار مشکل است. به طور مثال اینکه مود این آهنگ چگونه است تقریبا از سختترین اطلاعاتی است که میشود به دست آورد.
**۳- پیشنهاد بر اساس مدلهای ساخته شده:**
شامل الگوریتمهایی میشود که میتواند بر اساس اطلاعات به دست آمده آهنگ را پیشنهاد دهد.
# کارهای مرتبط
تا امروز چندین روش، الگوریتم و روند برای این سیستمها پیاده سازی و استفاده شدهاند که اگر بخواهیم از آنها دستهبندی کلی ارائه دهیم به موارد زیر میرسیم:
##روش فیلتر مشارکتی [^Collaborative Filtering] :
این متد یکی از موفقترین روشها است که تا کنون مورد استفاده قرار گرفته است. در این روش ما فرض میکنیم اگر دو کاربر *الف* و *ب* به تعدادی آیتم امتیاز مشابه دهند یا رفتار شبیه به همی داشته باشند، به آیتمهای دیگر هم امتیاز مشابهی خواهند داد. این روش سه زیرمجموعه دارد:
1. بر اساس حافظه: برای پیشبینی امتیاز آیتم جدید هر دفعه از تمامی تاریخچه کاربر و کاربران مشابهش استفاده میشود.
2. بر اساس مدل به دستآمده: در این روش با استفاده از امتیازات کاربران و افراد مشابه و همچنین استفاده از یادگیری ماشینی، مدلی به دست میآید که سلیقهی کاربر را توصیف میکند.
3. ترکیبی: از هر دو مدل بالا استفاده میکند. تجربه نشان دادهاست که این روش از هر دو روش دیگر بهتر نتیجه میگیرد.
**محدودیتها:**
+ سو گیری به سمت موارد محبوب(Popularity bias): معمولا آهنگهای محبوب، بیشتر امتیاز دریافت میکنند که این باعث میشود سیستم ما بیشتر موارد محبوب را پیشنهاد دهد که در نهایت با اینکه موفقیت آمیز است ولی کاربر را شگفتزده نمیکند.
+ شروع کند[^Cold start] : همیشه در مراحل اولیه تعداد امتیازهایی که داده میشود بسیار کم است و همین باعث میشود پیشنهاددهی عملکرد بدی پیدا کند. حتی در مورد آهنگهایی که معروف نیستند و یا مستقلاند احتمال اینکه پیشنهادداده شوند بسیار کم است زیرا هیچ امتیازی از آنها به دست نمیآید.
##روش محتوایی [^Content/Audio/Signal-based model] :
این روش بر خلاف روش قبلی سعی میکند با آنالیز و بررسی آهنگ آن را به کاربر پیشنهاد دهد. طوری که این روش ریشه در پردازش سیگنال و استخراج ویژگیهای آهنگ دوانده است. این الگوریتم آهنگی را که بیشترین شباهت را به تاریخچهی کابر را دارد پیدا کرده و به اون پیشنهاد میدهد.
**محدودیتها:**
+ با اینکه این روش مشکلات روش قبلی را حل میکند ولی محدودیتهای مخصوص به خودش را دارد. به طور مثال در این روش کل سیستم پیشنهاددهنده به اسختراج ویژگیها از سیگنال آهنگ وابسته میشود که خود مقوله بسیار مشکل و غیر قابل اعتمادی است. ضمن اینکه هنوز هیچ پژوهشی ثابت نکردهاست رفتار مشابه قبلی کاربر باعث میشود منجر به انتخاب آهنگ مشابه میشود.
##روش بر اساس متن [^Context-based model] :
در این روش بر خلاف دو روش قبلی به جای اتکا به محتوا و امتیازدهی کاربران، سعی میشود از نظرات عمومی منتشر شده (به طور مثال در شبکههای اجتماعی) استفاده شود تا آهنگ مورد نظر کاربر را پیشنهاد دهد.
این الگوریتم با استفاده از کاوش متنی محتوای مورد نظر مانند تشابه خوانندهها، ژانر، احساس و مود آهنگ، محیط معنایی آهنگ و ... را پیدا میکند و بر اساس آن مدل مورد نظر خود را میسازد. بعضی از تحقیقات نشان میدهد این روش میتواند ۲ روش قبلی را شکست دهد.
**محدودیتها:**
+ با اینحال این روش نیز سو گیری به سمت موارد محبوب و مشهور را نمیتواند رفع کند. ضمن اینکه وجود فیدبک برای آینمها در فضای اینترنت الزامی است.
##روش ترکیبی[^Hybrid model] :
در این الگوریتم چند روش قبلی با هم را ترکیب کرده و سعی میکند بهترین ترکیب ممکن را ارائه دهد. بنابراین با پیادهسازی درست میتواند هر روش را به طور جداگانه شکست داد و به محدودیتهای آن فائق آمد.
# آزمایشها
## شناخت دادهی موجود
دادهای که در این پروژه در اختیار داریم، از دیتاست یک میلیون آهنگ»[^Million Song Dataset] تهیه شدهاست. دیتاست اصلی خود به دلیل دیتای زیادی که هر آهنگ دارد حجم بسیار زیادی داراست(حدود ۳۰۰ گیگ). به همین دلیل ما از یک زیر مجموعه کوچکی که توسط وبسایت [kaggle](http://kaggle.com/c/msdchallenge) تهیهشده است استفاده میکنیم.
این دیتا شامل تاریخچهی آهنگ گوش دادن ۱ میلیون و ۱۱۰ هزار کاربر است. به این صورت که:
+ ۱ میلیون کاربر به عنوان دیتای آموزشی که تمام تاریخچهی آنها قابل مشاهده است.
+ ۱۱۰ هزار کابر به عنوان دیتای تست که فقط نصف تاریخچهی آنها قابل مشاهده است.
برای این پروژه ما نصف دیگر دیتای تست که قابل مشاهده نیست را قرار است پیشبینی کنیم.
تاریخچهی ارائه شده به این صورت است که هر سطر آن نشان میدهد هر کاربر به هر آهنگ چند بار گوش داده است:
user_id, song_id, listen_count
این دیتا نزدیک به ۳۸۰۰۰۰ آهنگ را پوشش میدهد. ماتریس کاربر-آهنگ نیز خیلی پراکندهاست به طوری که درصد دادههای غیر صفر فقط ٪۰.۰۱ است.
## تعریف دقیق مسئله
همانطور که گفته شد، وظیفهی ما این است که نصف دیگر از تاریخچهی دیتای تست را پیشبینی کنیم. این مشخصا معادل این است که آهنگهایی را پیشنهاد دهیم که پیشبینی میکنیم کاربر مایل است آن را گوش دهد.
از آنجایی که دادهی در اختیار ما شامل اطلاعات اساسی از خود آهنگ نمیشود (مانند فایل کامل آهنگ یا شکل کامل موج آن و مواردی از این قبیل) پس استفاده از روشهایی مثل فیلتر محتوایی[^Content Filtering] منطقی و حتی عملی به نظر نمیرسد. با این اوصاف بهترین راهحل موجود **فیلتر مشارکتی** است. در این روش از دو زاویهی مختلف میتوان به مسئله نگاه کرد. نگاه اول در بخش ۲.۱ توضیح دادهشده است؛ اما نگاه دیگری که میتوان داشت این است که اگر دو آهنگ *الف* و *ب* عموما با همدیگر گوشداده شوند (یا خریداری شوند) و یا بسیار شبیه به هم باشند اگر کاربر یکی از آنرا گوش کند (یا بخرد) بسیار محتمل است که دیگری را نیز گوش دهد(یا خریداری کند).
در نگاه تئوری این دو روش دقیقا باید معادل همدیگر باشند ولی در عمل اتفاقی که میافتد این است که نگاه دوم نتیجهی بهتری میدهد. شاید دلیل آن این باشد که پیداکردن شباهت یا اساسا تعریف آن بین دو آهنگ بسیار سادهتر است تا بین دو کاربر؛ چرا که عموما آهنگها متغیرهای زیادی ندارند به طور مثال معمولا سبک و خواننده و .. مطرح است اما ممکن است که یک کاربر موسیقی راک و فقط نوع خاصی از موسیقی کلاسیک دوست داشتهباشد و کاربر دیگر به موسیقی مدرن و عموما هر نوع کلاسیک علاقه داشتهباشد. همانطور که میبینیم پیدا کردن درصد شباهت دقیق بین این دو بسیار سخت است. بنابراین ما در این پروژه از **نگاه دوم** استفاده خواهیم کرد.
##استفاده از روش «فیلتر مشارکتی» به عنوان راه حل
تکنیکهای این روش از دادهساختاری به نام ماتریس کاربر-آهنگ استفاده میکنند. این ماتریس، ماتریس امتیازات هر فرد نسبت به هر آهنگ است.
در این مسئله، ما مجموعهی U از n کاربر و مجموعه I از m آهنگ داریم. ماتریس کاربر-آهنگ یا ماتریس امتیازدهی به صورت تعریف میکنیم.$$ R = {r_{iu}} \in R^{m\times n} $$ یعنی هر سلول از ماتریس حاوی امتیاز کاربر u به آهنگ i است. حال با توجه به ساختار دادهی در دست و نوع مسئله، ما این امتیاز را به صورت زیر در نظر میگیریم (فرض میکنیم):
$$ {r_{iu}} \in {0, 1} $$
در واقع مقدار ۱ نشان میدهد کاربر u آهنگ i را گوش دادهاست (یا تمایل دارد آنرا گوش دهد) و مقدار صفر هم نشاندهنده عدم تمایل یا گوش ندادن به آهنگ است. بنابراین ماتریس R به شکل زیر در میآید:
|آهنگ/کاربر|کاربر۱|کاربر۲|کاربر۳|...|
|:-|:-:|:-:||:-:|
|آهنگ ۱|1|1|1|...|
|آهنگ ۲|0|?|0|...|
|آهنگ ۳|1|1|?|...|
|...|...|...|...|...|
حال وظیفه ما اینطور تعریف میشود که میخواهیم مجموعهی k تایی (Q(u از آهنگهای جدیدی را پیدا کنیم که کاربر u با احتمال بیشتری تمایل دارد به آنها گوش دهد.
با توجه به حجم دادهای که در دست داریم میتوانیم از روش درونحافظهای[^In-Memory] برای فیلتر مشارکتی استفاده کنیم. در این روش ما برای پیشبینی آهنگها برای هر کاربر از کل ماتریس امتیازدهی R استفاده میکنیم. به این صورت که برای هر آهنگ ابتدا همهی آهنگهایی که بیشترین شباهت با آن را دارند پیدا میکنیم و سپس با جمعآوری این اطلاعات پیشبینی نهایی را انجام میدهیم. برای ترکیب و جمعآوری این آیتم ما از روش سادهی وزندار استفاده میکنیم و میزان شباهتها را با هم ترکیب میکنیم، برای هر کاربر u ما امتیاز آهنگ i (آهنگ i جدا از آهنگهایی است که قبلا گوش داده است) را به است صورت محاسبه میکنیم: [15]
$$ h(u,i) = \sum_{j \in I} f(w_{ij})r_{uj} = \sum_{j \in I(u)} f(w_{ij})$$
**(I(u:** آهنگهایی که کاربر قبلا گوش داده یا خریده است
**(w(i,j:** امتیاز شباهت آهنگ i و j
این فرمول به زبان ساده میگوید برای محاسبهی امتیاز آهنگ i، کافی است شباهت این آهنگ را با تمام آهنگهایی که کاربر قبلا گوش دادهاست حساب کنیم و آنها را با هم دیگر جمع بزنیم(ترکیب کنیم).
*نکته:* تابع (f(w وظیفه اینرا دارد که ترکیب مقادیر w را کنترل کند. به طور مثال ضریبی به در آن ضرب کند و یا ...
در نهایت برای محاسبهی آهنگهای پیشنهادی هر کاربر، کافی است به ازای همهی آهنگهای موجود، این امتیاز را حساب کنیم و بعد از مرتبسازی نزولی، k عنصر اول آنرا خروجی دهیم.
### تعیین معیار شباهتسنجی[^Similarity Measure] - محاسبهی (W(i,j
در مسئلههای «فیلتر مشارکتی» همیشه پیداکردن یک معیار شباهت از مهمترین و چالش برانگیز ترین بخشها بوده است. ایدهای رایجی که وجود دارد این است که نمیتوان یک معیار کلی پیدا کرد که بتواند در تمامی این مسائل جوابگوی ما باشد. بنابراین بر هر مسئله باید معیارمان کاملا با دقت انتخاب کنیم یا حتی اگر میتواتیم و منطقی هست خودمان ایجاد کنیم.
با توجه به مواردی که پیشتر گفته شد، در واقع (W(i,j میزان شباهت آهنگ i به آهنگ j است. با توجه به اینکه امتیازدهیها از مقادیر حسابی (و طیفی) به مقادیر باینری تبدیل کردیم، میتوانیم از این سادهسازی در فرآیند به دستآوردن معیار شباهت نهایت استفاده را بکنیم.
برای انجام آزمایشات و ارائه نسخهی اولیه معیار Cosine Similarity معیار بدی نیست و میتوان از آن استفاده کرد، ضمن اینکه این معیار برای موارد دارای تقارن نیز مناسب است. با توجه به اینکه مقادیر ما باینری هستند فرم نهایی آن به صورت زیر به دست میآید:
$$ w(i,j) = \frac{|U(i) \cap U(j)|}{ \sqrt[]{|U(i)|} \times \sqrt[]{|U(j)|} } $$
**(U(i:** مجموعه کاربرانی که به آهنگ i گوش داده بودند.
**(U(j:** مجموعه کاربرانی که به آهنگ j گوش داده بودند.
*نکته: در ادامه خواهیم گفت چرا این معیار، معیار خیلی مناسبی برای مسئلهی ما نیست.*
### چگونگی ترکیب (یا جمع زدن) (W(i,jها
در بخش ۳.۳ دیدیم که امتیاز نهایی توسط تابع (h(u,i محاسبه میشود. حال خود این تابع نیز حاصل جمع بخشهای کوچکتری از امتیاز هست. نکتهی مهم اینجاست که ما باید بتوانیم تاثیر هر کدام از این بخشهای کوچک را در امتیاز نهایی تعیین کنیم. این همان کاری است که تابع (f(w انجام میدهد.
یکی اهدافی که میتوان برای این تابع در نظر گرفت این است که ما میخواهیم تاثیر امتیازهای بزرگتر را بیشتر و تاثیر امتیازهای کوچکتر را کم بکنیم.
برا این کار میتوان از تابع زیر استفاده کرد.
$$ f(w) = w^t, t \in N $$
یا حتی تابع (log(w- نیز میتواند به ما کمک کند اما یکی از مشکلاتی که دارد این است که مقادیر نزدیک به ۱ را بسیار بزرگ میکند و ضمن اینکه محاسبهی آن نیز زمانبر است.
![تصویر ۱-۳: نمودار توانهای ۲،۳،۴ و ۱۰](https://boute.s3.amazonaws.com/263-Unknown2.png)
تا به اینجا تمامی الگوریتمهای مورد نیاز برای پیادهسازی با جزئیات گفتهشد. حال با توجه به موارد گفته شده اقدام به پیادهسازی میکنیم.
#پیادهسازی و آزمایش
در این بخش ابتدا معیارهایی برای اندازهگیری سامانه بیان کرده و نتایج اولیه را محاسبه و گزارش میکنیم. سپس مواردی را برای بهبود سامانه مشخص میکنیم و پس از پیادهسازی این موارد، نحوهی آن را به طور کامل شرح میدهیم و در نهایت هم نتایج را با استفاده از نسخهی بهبودیافته دوباره محاسبه میکنیم.
## تعیین معیار برای اندازهگیری دقت سامانه
**۱- معیار Mean Average Precision:**
با توجه به ساختار مساله، از معیار Mean Average Precission استفاده میکنیم. استفاده از این معیار باعث میشود عملکرد کل سیستم را با یک متغیر تک مقداره اندازه بگیریم. به زبان ساده این معیار به ما میگوید که سیستم ما برای هر کوئری چهقدر خوب کار میکند.
در این معیار اگر ما n کوئری به سیستم داده باشیم، برای محاسبهی mAp تنها کافی است میانگین AveragePrecision ها برای هر کوئری به دست آوریم:
$$ mAP = \frac{\sum_{q=1}^Q AveP(q)}{Q}$$
**توضیح در مورد Average Precision:**
برای توضیح این متغیر، ابتدا باید مفهوم Precision را بفهمیم، Precision به ما میگوید که از بین تمام نتایجی که سیستم به عنوان مرتبط تشخیص داده است، چه کسری از آنها واقعا درست بوده است. حال Average Precision برای هر کوئری به این معنا است: اگر فرض کنیم سیستم ما ۲۰ عدد آیتم مرتبط پیدا کردهباشد و در واقع فقط ۱۰ آیتم از اینها درست باشد، باید به ازای هر کدام از این ۱۰ تا یکبار Precision را محاسبه کنیم و در نهایت از آنها میانگین بگیریم.
![تصویر ۱-۴: مثال از نحوهی محاسبه Average Precision](https://boute.s3.amazonaws.com/263-avp.jpg)
حال در مسالهی ما همهی اینها به این معناست اگر برای کاربری ۵۰۰ عدد موسیقی را پیشنهاد داده باشیم، این ۵۰۰ آهنگ آیتمهای مرتبط ما است و آیتمهای واقعا درست آهنگهایی است که کاربر آنها را قبلا گوش کرده است(این آیتمها از قسمت پنهان دیتای تست به دست میآید که در دسترس ما قرار دادهشدهاند. ر. ک. بخش ۳.۱) حال AveP را به راحتی برای آن حساب میکنیم.
**۲- معیار Mean Recall:**
برای هر فرد درصد آیتمهای درست پیدا شده نسبت به آیتمهایی که میدانیم گوشداده است، را به دست میآوریم. به عبارت دیگر این معیار نسبت آیتمهای درست پیدا شده به کل آیتمهای درست است(برا هر فرد).
## نتایج اولیه
با توجه به موارد گفته شده در بخش ۳.۱، سیستم ما قرار است برای تعدادی کاربر که نصف تاریخچهی آن به عنوان قسمت قابل مشاهده موجود است، نصف دیگر که از برنامه پنهان است آهنگ پیشنهاد کند. در نهایت موارد پیشنهاد شده با نصفهی دیگر(پنهان) دیتا مقایسه میشود.
برای این فاز ما ۱۰ هزار کاربر اول از دیتای تست را جدا میکنیم و نتایج را برای آنها محاسبه میکنیم.
*نکته برای هر کاربر ما ۵۰۰ آهنگبرتر را پیشنهاد میدهیم.*
| روش پیادهسازی | معیار Mean Recall | معیار Mean Average Precision |
|:--------------|:------:|:-------------:|
| نسخهاولیه-۱۰هزار کاربر اول | %53.942 | 0.218914 |
## بهبود سامانه
برای هر سیستمی اصولا دو زمینهی شاخص برای بهبود وجود دارد:
1. دقت سامانه
2. سرعت
در این فاز سعی شدهاست در هر دو زمینه بهبودهایی داشته باشیم.
### دقت سامانه
همانطور که پیشتر گفته شد یکی از مواردی که خیلی جای بهبود دارد، الگوریتم پیدا کردن شباهت بین آهنگها (یا به طور کلی بین آیتمها در یک سیستم CF) است. در این قسمت برای بهبود از چند الگوریتم دیگر استفاده میکنیم.
به طور کلی ۲ نوع معیار اندازهگیری شباهت وجود دارد؛ **متقارن** و **نامتقارن**. در شباهتسنجی متقارن فرض میشود که میتوان جای ۲ آیتم را عوض کرد یا به عبارت دیگر شباهت اولی به دومی همان شباهت دومی به اولی است. Cosine Similarity و Jacard Distance و ... از این نوع هستند.
از طرف دیگر معیارهای نامتقارن را داریم. این معیارها فرض گفته شده را در نظر نمیگیرند به عبارتی شباهت اولی با دومی با شباهت دومی با اولی فرق دارد و مقدار متفاوتی را به ما میدهد. برای فهم بهتر آن میتوان از ۲ مثال ذیل استفاده کرد:
> کرهی شمالی به چین، بیشتر شبیه است تا چین به کرهی شمالی.
> بازیکنان تیم ملی شبیه یوزپلنگان تیزپا هستند. یوزپلنگها شبیه بازیکنان تیم ملی هستند.
این مثال به ما میگوید که بیشتر ویژگیهایی که کرهشمالی دارد در چین هم پیدا میشود ولی بیشتر ویژگیهای چین در کرهی شمالی یافت نمیشود، در نتیجه کرهی شمالی بیشتر به چین شبیه است تا چین به کرهی شمالی. در مثال بعدی بازینکنان از لحاظ سرعت شبیه یوز پلنگ هستند اما در حالت کلی یوزپلنگها شباهت زیادی با بازیکنان تیم ملی ندارند. در واقع کلید تفسیر این است که یک طرف به شکلی دیگری را شامل میشود. یه به عبارت سادهتر مجموعه ویژگیهای یک طرف زیر مجموعه ویژگیهای طرف دیگر است.
حال در مسئلهی ما مصداق نامتقارن بودن این است که ما شباهت هر آهنگ را با آهنگهایی که کاربر قبلا گوش دادهاست قرار است حساب کنیم و شبیهترینها را به او پیشنهاد دهیم. در واقع ما بیشتر دوست داریم که ببینیم این آهنگ پیشنهادی چهقدر مورد اقبال کاربر قرار خواهد گرفت با فرض اینکه بدانیم همان کاربر *قبلا* این آهنگها را گوشداده و دوستداشته است. خب این مشخصا یک مسئله از نوع متقارن نیست. بنابراین یکی از موارد بهبود سیستم میتواند استفاده از معیارهای شباهتسنجی نامتقارن باشد. طبق [11] و [12] از معیارهای زیر میتوان استفاده کرد:
**۱- Tversky index:**
این یک معیار نا متقارن است که برای محاسبهی شباهت نامتقارن بین دو مجموعه استفاده میشود.
$$ S(X,Y) = \frac{| X\cap Y|}{|X \cap Y| + \alpha |X-Y| + \beta |Y - X| } $$
که α + β = 1. در این معیار پروتوتایپ و نمونه تعریف میشود اگر X را پروتوتایپ در نظر بگیریم و Y را نمونه، با فرض اینکه X زیر مجموعه Y باشد (ینی تمام ویژگیهای پدر در نمونه هم موجود باشد) و همچنین α = 1 و β = 0 آنگاه معیار ما برابر با ۱ خواهد شد. بنابراین میتوانیم به تغییر مقادیر α و β بایاس معیار را به سمت پروتوتایپ و نمونه تغییر دهیم.
**۲- Asymmetric Cosine Similarity:**
طبق معیار قبل برای نامتقارنکردن میتوانیم از روش زیر استفاده کنیم، یعنی با استفاده از پارامتر α میتوانیم تاثیر هر کدام از مجموعهها را کنترل کنیم:
$$ aCosSim(X,Y) = \frac{| X\cap Y|}{|X|^{\alpha} |Y|^{1 - \alpha} } $$
همانطور که میبینیم هرچه α بیشتر شود، تاثیر X در کوچککردن معیار بیشتر میشود. بنابراین شاید بتوان با کم کردن تاثیر اندازهی مجموعهی آهنگ غیر از تاریخچهی کاربر به نتایج بهتری رسید.
نکات مهم در این معیارها این است که باید پارامترها انتخاب شوند که خب نیازمند آزمون و خطا برای پیدا کردن بهترین مقدار است.
**۳- سایر معیار های متقارن:**
سایر معیارهای متقارن مثل Jacard، Dice و Tanimoto را هم آزمایش میکنیم تا ببینیم کدام دسته از معیارهای شباهتسنجی میتواند عملکرد بهتری داشته باشد. متقارن یا نامتقارن.
### سرعت سامانه
برای بهبود عملکرد سامانه از لحاظ سرعت، چند قسمت مورد بازبینی و بهبود قرار گرفتند. برای نائلآمدن به این هدف و همچنین میزان تغییرات زیاد قسمت زیادی از کد برنامه از ابتدا و با توجه به بهینهسازیها بازنویسی شد.
*نکته: نسخهی اولیه با برنچ v1 و نسخهی بازبینی شده با برنچ v2 در گیت قرار دارد.*
**۱- موازی سازی:**
از آنجایی که پیشنهاد یه یک کاربر مستقل از پیشنهاد یه کاربر دیگر است میتوان خیلی راحت این دو عمل را به صورت موازی انجام داد. به این صورت که ابتدا دادهها را از فایل میخوانیم و بعد از ساختن دادهساختار مناسب برای نگهداری آنها، به تعداد هستههای سیپییو ترد میسازیم و هر ترد از صفی که کاربران را نگه میدارد بر میدارد و پس از محاسبهی نتایج، آنها را در صف نتایج میگذارد. همٰزمان با این، ترد دیگری از صف نتایج بر میدارد و آنها را در فایل ذخیره میکند.
**۲- بهینهسازی در سرعت خواندن دادهها:**
با اینکه این قسمت از برنامه فقط یکبار اتفاق میفتد اما زمان زیادی را ممکن است به خود اختصاص دهد برای همین بهینهسازی این بخش هم جای دوری نمیرود. در این بخش تاثیرگذار ترین مورد، بهینهسازی دادهساختارهای ذخیرهسازی است. اولین کار هم این است که با توجه به سایز داده که از قبل اندازه گرفتیم میتوانیم دادهساختارها را از قبل PreAllocate بکنیم که از افرایش سایز هنگام ساختن آنها جلوگیری شود. مورد دیگر هم یکی کردن چندین لوپ از این قسمت از برنامه است به این صورت که فقط با یک بار خواندن از فایل مواردی که نیاز داریم را بسازیم.
**۳- بهینهسازی در حجم دادهها و دسترسی به آنها:**
در نسخهی ابتدایی برنامه هر آهنگ یک آیدی رشتهای به طول ۱۸بایت داشت. همچنین هر کاربر هم آیدی رشتهای به طول ۴۰ بایت داشت.
خب نگهداری و مقایسهی اینها هر کدام هزینهی زیادی از ما میگرفت. از همینرو به جای این به هر آهنگ و همچنین به هر کاربر یک عدد اساینکردیم. این روش چند خوبی داشت: اولا هزینهی نگهداری آن بسیار کمتر میشد، دوما نیاز به داشتن HashMap برای نگه داشتن دیتای هر آهنگ از بین میرفت و میتوانستیم از یک آرایه ساده استفاده کنیم که دیتای هر آهنگ در یک اندیس آن ذخیره میشد. سوما مقایسهی اینها بسیار سریعتر انجام میگرفت.
**۴- بهنیهسازی الگوریتم اشتراک بین دو مجموعه:**
یکی از موارد هزینهبر در برنامهی ما محاسبهی شباهت بین آهنگهاست چون بار و بارها انجام میپذیرد. برای محاسبهی شباهت بین آهنگها باید ابتدا اشتراک بین کاربرانی که این دو آهنگ را گوش میکنند محاسبه کنیم. در نسخهی اولیه از `std::unorderd_set` استفاده میشد که در پشت آن از HashTable استفاده میکرد. با توجه به اندازهگیریهای گرفته شده برای به دستآوردن اشتراک بین دو مجموعه بهتر است آنها را به صورت وکتورهایی مرتبشده دربیاوریم که اشتراک این دو را با الگوریتمی شبیه merge_sort به راحتی میتوان به دست آورد. که از لحاظ زمانی و مکانی بسیار به صرفهتر در خواهد آمد.
## تشریح و توضیح پیادهسازی
توضیح پیادهسازی بر روی نسخهی بهبود یافته انجام میپذیرد با این حال نسخهی اولیه روی برنچ `v1` قابل دسترس است.
**تکنولوژیهای مورد استفاده:**
با توجه به ساختار داده موجود و همچنین ساختار الگوریتمها نیازی به استفاده از کتابخانهی خاصی احساس نشد. بنابراین این سیستم با زبان **سیپلاسپلاس نسخهی ۱۴** پیادهسازی شده است و تنها از لایبریهای استاندارد زبان استفاده میکند. همچنین در زمینهی موازیسازی برای افزایش سرعت از کتابخانهی **Intel Threading Block** استفاده شده است که عملکرد بهینهتری را در اختیار برنامهی ما میگذارد.
سامانهی توصیهگر ما از چندین بخش اصلی و مهم تشکیل شده است که شامل ۱- دادهساختارهای مناسب نگهداری دیتای یادگیری موجود ۲- قسمت مربوط به پرکردن و ساختن دادهساختار مورد نیاز ۳- قسمت مربوط به محاسبهی آهنگهای پشنهادی ۴- تابع اندازهگیری شباهت ۵- قسمت مربوط به موازیسازی
۱- **دادهساختارهای مناسب نگهداری دیتای یادگیری موجود**
![](https://boute.s3.amazonaws.com/263-ds.jpg)
متغیر `songs` مهمترین داده ساختار ماست به این صورت است که برای هر آهنگ ما آیدی تمام کاربرانی را که آن را گوش دادهاند نگه میداریم. همانطور که قبلا گفته شد هر کاربر و همچنین هر آهنگ با یک عدد نمایش داده میشوند. متغیر `evalUsers` تاریخچهی کاربرانی است که میخواهیم به آنها پیشنهاد دهیم و در آخر هم برای جلوگیری از محاسبهی چند باره، آهنگهای پرطرفدار را بر اساس تعداد کاربرانی که به آنها گوش میدهند در متغیر `popularSongs` نگه میداریم:
songs: [
23 -> [12, 15, 28, 39, 229, 3339, 4499, 5999, ... ]
92 -> [3, 4, 23, 39, 145, 149, 233, 245, 267, ... ]
...]
evalUsers: {
"6e91e25..." -> {12, 33, 23, 2, 8, 9, 11, 202, 344349, 23, ..}
"daa9e7e..." -> {230, 2, 8, 40, 1,33, 903, 85, 3332, 8304, ..}
...}
popularSongs: [22, 323, 534, 4554, 2932, 3443, 32, 2323]
**۲- قسمت مربوط به پرکردن و ساختن دادهساختار مورد نیاز:**
![](https://boute.s3.amazonaws.com/263-init_data2.png)
در این قسمت ما قرار است `songs` و `evalUsers` و `popularSongs` را پر کنیم، مثال بالا مربوط به پر کردن متغیر دوم است. یکی نکات بهینهسازی که مشاهده میشود `(set.reserve(20` است چرا که با اندازهگیری فهمیدیم به طور میانگین هر کاربر به ۲۰ آهنگ گوش داده است.
در اینجا `evalUsers` یک HashMap است که خیلی ساده به ازای هر خط از فایل، آهنگ گوشداده شده را به کاربر مربوطه اضافه میکنیم، سایر متغیرها نیز با شبیه به همین پر میشوند.
**۳- قسمت مربوط به محاسبهی آهنگهای پشنهادی:**
![](https://boute.s3.amazonaws.com/263-rec_01.png)
این متد از چندین بخش تشکیل شدهاست. بخش اول ابتدا بررسی میکنیم که کاربر در دیتاست ما وجود دارد یا خیر، اگر ما تاریخچهی او را نداشتیم تنها میتوانیم آهنگهای پر طرفدار رو به او بدهیم. اگر کاربر در دیتاست موجود بود تاریخچهی او را لود میکنیم.
![](https://boute.s3.amazonaws.com/263-rec_022.png)
این بخش درواقع هستهی اصلی سامانهی توصیهگر ماست که دقیقا پیاده سازی بخش ۳.۳ است. به این صورت که ما برای هر کدام از ۳۸۰۰۰۰ آهنگی که داریم عددی را محاسبه میکنیم. به طور مثال برای آهنگ الف، ابتدا شباهتش را با تمام آهنگهایی که کاربر قبلا گوشداده محاسبه میکنیم و سپس همهی اینها را با هم جمع میکنیم. این حاصل جمع امتیاز آهنگ الف است که همانطور که مشاهده میشود در متغیر `[scores[songId` نگهداری میشود.(البته با توجه به بخش ۳.۳.۲ این امتیاز از حاصل جمع «شباهت به توان ۳»ها به دست میآید)
![](https://boute.s3.amazonaws.com/263-rec_03.png)
در آخر بعد از مرتبسازی آهنگها بر اساس `scores`، آنها آمادهی خروجی دادن میشوند به با این نکته که قبل از برگرداندن آنها باید چک کنیم که جزو تاریخچهی قبلی کاربر نباشد(زیرا میخواهیم آهنگ جدید پیشنهاد کنیم).
**۴- تابع اندازهگیری شباهت:**
![](https://boute.s3.amazonaws.com/263-sim.png)
این تابع تقریبا مهمترین قسمت و تعیینکنندهی دقت سامانه است. برای محاسبهی شباهت ابتدا لیست کاربرانی که این دو آهنگ گوشدادهاند رو بارگزاری میکنیم (متغیر `[songs[sId1` و `[songs[sId2`)، سپس با توجه به الگوریتمی که در بخش ۴.۳.۲ گفته شد اشتراک بین این ۲ مجموعه را به دستآوریم. در نهایت هم در خط آخر با توجه به فرمول این معیار(در این مثال Cosine Similarity) مقدار آنرا محاسبه میکنیم.
نکتهای که در این بخش باید توجه کنیم این است که `[songs[sId1` و `[songs[sId2` بر اساس آیدی کابران مرتبسازی شدهاند.
**۵- قسمت مربوط به موازیسازی:**
![](https://boute.s3.amazonaws.com/263-p01.png)
این بخش بسیار ساده است. ما به تعداد هستههای پردازنده ترد میسازیم و همه به غیر از یکی از آنها مسئول محاسبهی نتایج میکنیم. ترد دیگر هم وظیفهاش ذخیره سازی نتایج است.
![](https://boute.s3.amazonaws.com/263-p02.png)
این ترد از صف انتظار کاربرانی که باید نتایج را برای آنها محاسبه کند(`usersQueue`) برمیدارد و پس از محاسبه نتایج توسط تابع `(recommend(user` که پیشتر گفته شد، نتایج را در صف مربوط به نتایج میریزد(`resultQueue`).
![](https://boute.s3.amazonaws.com/263-finalll.png)
و در آخر این ترد مسئول خواندن نتایج از صف و ذخیرهی آنها در فایل با فرمت زیر است:
userId__song1,song2,song3,...
2baf0a17a...__12,32439,203084,345094,3474,838,203,422,...
...
**نکته:** برای نحوهی اجرای کد به گیت مراجعه کنید.
+ [پروژه در گیتهاب (نسخهی بهبود یافته و نسخهی اولیه)](http://github.com/kazemnejad/music-recommender)
+ [لینک نسخهی اولیه (برنچ v1 در گیت)](https://github.com/kazemnejad/music-recommender/tree/v1)
## نتایج نسخهی بهبود یافته
در دو زمینهی آزمایشات را روی نسخهی جدید انجام میدهیم:
###دقت در نسخهی بهبود یافته
آزمایشهای انجام شده در این قسمت به ۲ بخش تقسیم میشود، برای هر آزمایش، ۱۰۰۰ نفر اول دیتای تست را ورودی دادیم:
**۰- شباهتسنجی Cosine (به عنوان معیار برای سایر آزمایشها):**
|#|معیار Mean Recall|معیار Mean Average Precision|
|:----------|:--------:|:---------:|
|آزمایش ۱|%53.486|0.223802|
**۱- شباهتسنجی Tversky index:**
|#|α|β|معیار Mean Recall|معیار Mean Average Precision|
|:--------------|:------:|:------:|:------:|:-------------:|
|آزمایش ۱|1.0|0.0|%56.87|0.247733|
|آزمایش ۲|0.95|0.05|%56.89|0.249844|
|آزمایش ۳|0.90|0.10|%56.65|0.248108|
|آزمایش ۴|0.8|0.2|%55.89|0.229856|
|آزمایش ۵|0.65|0.35|%55.29|0.233935|
![تصویر ۲-۴ ](https://boute.s3.amazonaws.com/263-gtv.png)
همانطور که در نمودار ۲-۴ مشاهده میشود بهترین نتایج در پارامترهای α = 0.95 و β = 0.05 به دست میآید.
**۲- شباهتسنجی Asymmetric Cosine Similarity:**
|#|α|معیار Mean Recall|معیار Mean Average Precision|
|:--------------|:------:|:------:|:-------------:|
|آزمایش ۱|0.05|%57.02|0.251896|
|آزمایش ۲|0.10|%57.15|0.252625|
|آزمایش ۳|0.12|%57.16|0.253373|
|آزمایش ۴|0.15|%57.123|0.251734|
|آزمایش ۵|0.18|%56.91|0.250638|
|آزمایش ۶|0.25|%56.66|0.246807|
![تصویر ۳-۴](https://boute.s3.amazonaws.com/263-gasym.png)
همانطور که در نمودار ۳-۴ مشاهده میشود بهترین نتایج در پارامترهای α = 0.12 به دست میآید.
**۳- سایر:**
|#|معیار Mean Recall|معیار Mean Average Precision|
|:----------|:--------:|:---------:|
|شباهتسنجی Jacard|%54.144|0.219231|
|شباهتسنجی Dice|%54.17|0.223455|
|شباهتسنجی Tanimoto|%54.14|0.219231|
**جمعبندی:**
![تصویر ۴-۴](https://boute.s3.amazonaws.com/263-fmrec.png)
![تصویر ۵-۴](https://boute.s3.amazonaws.com/263-fmap.png)
در ۴-۴ و ۵-۴ نتایج همهی معیارها کنار هم گذاشتیم و همانطور که مشاهده میشود، الگوریتمهای نامتقارن با فاصلهی نسبتا خوبی نتایج بهتری کسب کردند. در بین الگوریتمهای نامتقارن هم معیار AsymCosSim توانست با اختلاف کمی امتیاز بیشتری نسبت به Tversky کسب کند. با این حال برای ادامهی بهبود دادن بهتر است بیشتر روی معیارهای نامتقارن کار شود و سعی شود پارامترهای این معیارهای به دست آمده بهینهتر شوند تا در مجموع نتایج بهتری را داشته باشیم.
###سرعت در نسخهی بهبود یافته
آزمایشهای این بخش همگی روی سختافزار زیر انجام شدهاند. هر کدام ۵ بار تکرار شده و میانگین به عنوان نتیجه گذاشته شدهاست:
Apple MacBook Pro 15 inch mid 2017
Intel Core i7 2.8 GHz (7700HQ)
16 GB ram
SSD storage
macOS 10.12.6
**۱- سرعت خواندن دادهها:**
هر ۲ نسخه برنامه همهی دیتاهای زیر را خوانده و در دادهساختار مناسب میریزند.
File Size # of lines
train_triplets.txt 3GB 48,373,586
kaggle_visible_evaluation_triplets.txt 90MB 1,450,933
kaggle_songs.txt 9.9MB 386,213
kaggle_users.txt 4.5MB 110,000
|#|نسخهی اولیه|نسخهی نهایی|بهبود|
|:-------------|:--------:|:--------:|:---------:|
|آزمایش سرعت خواندن دادهها|321.647s|130.542s|۱۴۶٪ سریعتر|
**۲- اشتراک بین مجموعهها:**
۲ مجموعه هر دو شامل ۱۰۰۰۰۰۰ عضو که شامل ۳۳۳۳۳۴ عضو مشترک میباشند به عنوان دیتای تست در نظر گرفته شدهاست.
|#|نسخهی اولیه|نسخهی نهایی|بهبود|
|:-------------|:--------:|:--------:|:---------:|
|آزمایش اشتراک مجموعهها|47638μs|1610μs|۲۹برابر سریعتر|
**۳- سرعت محاسبهی نتایج برای ۱۰۰ ورودی:**
به هر دو نسخه، ۱۰۰ نفر اول ،به عنوان ورودی داده شده است زمان محاسبهی نتایج را اندازه میگیریم:
|#|نسخهی اولیه|نسخهی نهایی|بهبود|
|:-------------|:--------:|:--------:|:---------:|
|آزمایش نتایج برای ۱۰۰ ورودی|6186.11s|1225s|۶برابر سریعتر|
# کارهای آینده
تا اینجا ما توانستیم نتایج به نسبت خوبی را به دستآوریم. اما یکی از چیزهایی که در سامانهی مبتنی بر CF باید همیشه مورد توجه قرار گیرد مسئلهی شروع کند است. از آنجایی که یک معیار شباهتسنجی نمیتواند بهترین عملکرد خود را هم در شروعکند و هم در غیر آن داشته باشد باید سراغ سیستم هیبریدی برویم که بتواند بین این دو سیوئیچ کند و در زمان مناسب با توجه به تعداد آیتمها بهترین عملکرد را برایمان به ارمغان آورد. همانطور که در [13] اشاره شده است میتوان معیار شباهتسنجی برای شروعکند طراحی کرد که در مواقع نیاز به آن سوئیچ بکنیم.
یکی از کارهای دیگری که میتوان انجام داد و قطعا عملکرد سیستم را خیلی بهبود میدهد پردازش سیگنال آهنگ و استخراج ویژگیهای صوتی از آن است. که خب این شاخهی بسیار بزرگ و با تاریخچهای است. حتی تکنیکهای جدیدی هم معرفی شدند که تصویر سیگنال را با استفاده از روشهایی به دست میآورند و با استفاده از یادگیری عمیق به استخراج آهنگهای مشابه میپردازند و با توجه به سلیقهی کاربر به او پیشنهاد میدهند.
البته سایر متغیرها مثل ساعت گوشدادن و یا مود کاربر هم امروز در سیستمهای تجاری استفاده میشود که خب تاثیر بهسزایی در تجربهی کاربر میگذارند و با توجه به دیتاهای موجود میتواند زمینهی مفیدی برای ادامهی کار واقع شوند.
# نتیجه نهایی
در این پروژه ابتدا مسئلهی سامانه توصیهگر آهنگ به طور کامل شرح دادیم، سپس راهحلهای موجود را بررسی کردیم، در نهایت با توجه به دیتای موجود مجبور شدیم از روش CF را انتخاب کنیم. از چندین الگوریتم شباهتسنجی استفاده کردیم و پارامترهای مناسب را پیدا کردیم و در نهایت با بهبودهایی که انجام شد توانستیم به نتیجهی مطلوی برسیم. نتایج نشان داد که برای مسئلهی ما، علی الخصوص دیتای موجود عملکرد شباهتسنجیهای نامتقارن بسیار بهتر است و میتوان با بهبود و استفاده از آنها به نتایج مناسبی رسید.
# مراجع
[1] Yading Song, Simon Dixon, and Marcus Pearce: A Survey of Music Recommendation Systems and Future
Perspectives, Centre for Digital Music Queen Mary University of London
[2]Dawen Liang, Minshu Zhan, and Daniel P. W. Ellis: CONTENT-AWARE COLLABORATIVE MUSIC RECOMMENDATION USING PRE-TRAINED NEURAL NETWORKS, LabROSA, Dept. of Electrical Engineering Columbia University, New York
[3] Florian Strub Hybrid, Romaric Gaudel, Romaric Gaudel: Recommender System based on Autoencoders
[4] Aa ̈ron van den Oord, Sander Dieleman, Benjamin Schrauwen: Deep content-based music recommendation, Electronics and Information Systems department (ELIS), Ghent University
[5] Katherine Ellis, Emanuele Coviello, Antoni B. Chan, and Gert Lanckriet: A Bag of Systems Representation for Music Auto-Tagging
[6] Armin Namavari, Blake Howell, Gene Lewis: Predicting Similar Songs Using Musical Structure
[7] Yonatan Vaizman, Brian McFee, Member, IEEE, and Gert Lanckriet: Codebook-Based Audio Feature Representation for Music Information Retrieval
[8] Katherine Ellis, Emanuele Coviello, Emanuele Coviello: SEMANTIC ANNOTATION AND RETRIEVAL OF MUSIC USING A BAG OF SYSTEMS REPRESENTATION
[9] Brian McFee, Student Member, IEEE, Luke Barrington, and Gert Lanckriet, Member: Learning Content Similarity
for Music Recommendation
[11]Ankita Garg, Catherine G. Enright, Michael G. Madden:On Asymmetric Similarity Search
[12]Daniel P. Faith: Asymmetric binary similarity measures
[13] Hyung Jun Ahn: A new similarity measure for collaborative filtering to alleviate the new user cold-starting problem
[15]Xiaoyuan Su and Taghi M. Khoshgoftaar, "A Survey of Collaborative Filtering Techniques", Department of Computer Science and Engineering, Florida Atlantic University, August 2009, 421425,
[Spotify’s Discover Weekly: How machine learning finds your new music](https://hackernoon.com/spotifys-discover-weekly-how-machine-learning-finds-your-new-music-19a41ab76efe)
[Music Search and Recommendation from Millions of Songs](https://www.youtube.com/watch?v=RIW7jjurpPI)
[Building a Music Recommender with Deep Learning](http://mattmurray.net/building-a-music-recommender-with-deep-learning/)