{ "cells": [ { "cell_type": "markdown", "id": "d79795ff", "metadata": {}, "source": [ "## Introduction" ] }, { "cell_type": "markdown", "id": "555745b9", "metadata": {}, "source": [ "이 노트북은 Scikit-Learn 및 MXNet을 사용하여 Content 기반의 추천 시스템을 구축하는 방법을 보여줍니다. Collaborative Filtering 기반의 추천 시스템의 예는 다른 노트북, [mxnet-gluon-recommender](.mexnet-gluon-recommender.ipynb) 를 참고하세요. 또한, 이 노트북은 SageMaker Notebook 에서 로컬 환경의 인스턴스를 활용합니다.\n", "\n", "이 추천 시스템은 각 뉴스 기사의 내용을 바탕으로 상위 N 개의 추천 뉴스 기사를 요청합니다." ] }, { "cell_type": "markdown", "id": "4ad35827", "metadata": {}, "source": [ "## News Article Recommendation System" ] }, { "cell_type": "markdown", "id": "54c518bb", "metadata": {}, "source": [ "데이터셋은 하위 Kaggle 사이트에서 다운로드 받을 수 있습니다.\n", "\n", "https://www.kaggle.com/datasets/snapcrack/all-the-news?resource=download" ] }, { "cell_type": "markdown", "id": "cb75a912", "metadata": {}, "source": [ "데이터를 다운받은 후 data 폴더를 만들고 csv 파일들을 저장합니다." ] }, { "cell_type": "markdown", "id": "acd9ee86", "metadata": {}, "source": [ "## Collaborative Filtering 과 Content-based Filtering\n", "\n", "Collaborative Filtering 은 'cold start' 문제로 어려움을 겪습니다. 즉, 시스템에 사용자 데이터가 충분히 없는 경우 시스템에서 적절한 권장 사항을 작성하는 데 어려움이 있습니다. 일반적인 해결책은 제품데이터를 “전면 적재” 하는 것입니다. \n", "\n", "지금 막 시작했거나 최소한의 데이터를 보유한 비즈니스의 경우 Content-based 추천 시스템은 사용자에게 추천을 제공하는 CF 의 대안입니다.\n", "\n", "Content-based 추천 시스템은 사용자별로 고도로 맞춤화되어 있지만 콜드 스타트 문제는 존재하지 않습니다. 이 때문에 비즈니스는 데이터가 많지 않은 초기 단계부터 추천을 제공 할 수 있습니다." ] }, { "cell_type": "markdown", "id": "7604ce35", "metadata": {}, "source": [ "## Preparation" ] }, { "cell_type": "code", "execution_count": 17, "id": "3bf6c3ff", "metadata": {}, "outputs": [], "source": [ "import pandas as pd\n", "import glob\n", "import os\n", "import numpy as np\n", "\n", "from sklearn.feature_extraction.text import TfidfVectorizer\n", "import mxnet as mx" ] }, { "cell_type": "code", "execution_count": 4, "id": "00a61609", "metadata": {}, "outputs": [], "source": [ "file_path = \"data/\"\n", "all_files = glob.glob(file_path + \"*.csv\")" ] }, { "cell_type": "code", "execution_count": 5, "id": "7b672d4c", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['data/articles1.csv']" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "all_files" ] }, { "cell_type": "code", "execution_count": 6, "id": "2c2c8950", "metadata": {}, "outputs": [], "source": [ "extract_features = lambda f : pd.read_csv(f, usecols = [\"id\", \"title\", \"publication\", \"content\"])" ] }, { "cell_type": "code", "execution_count": 7, "id": "ea21422f", "metadata": {}, "outputs": [], "source": [ "articles = pd.concat((extract_features(f) for f in all_files))\n" ] }, { "cell_type": "code", "execution_count": 8, "id": "f0948cd8", "metadata": {}, "outputs": [], "source": [ "articles = articles.head(1000)" ] }, { "cell_type": "code", "execution_count": 9, "id": "1b5710c3", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
idtitlepublicationcontent
017283House Republicans Fret About Winning Their Hea...New York TimesWASHINGTON — Congressional Republicans have...
117284Rift Between Officers and Residents as Killing...New York TimesAfter the bullet shells get counted, the blood...
217285Tyrus Wong, ‘Bambi’ Artist Thwarted by Racial ...New York TimesWhen Walt Disney’s “Bambi” opened in 1942, cri...
317286Among Deaths in 2016, a Heavy Toll in Pop Musi...New York TimesDeath may be the great equalizer, but it isn’t...
417287Kim Jong-un Says North Korea Is Preparing to T...New York TimesSEOUL, South Korea — North Korea’s leader, ...
...............
99518412Russians Implicated in Doping Still Compete, A...New York TimesMany of the world’s winter athletes, now prepa...
99618413Mother Jones Is Named Magazine of the Year - T...New York TimesMother Jones was named magazine of the year on...
99718414Florida Woman Whose ‘Stand Your Ground’ Defens...New York TimesMarissa Alexander no longer wears an ankle mon...
99818416First Amendment Support Climbing Among High Sc...New York TimesSupport among American high school students fo...
99918417Damien Chazelle, ‘La La Land’ Director, on Cal...New York TimesDamien Chazelle, the writer and director of th...
\n", "

1000 rows × 4 columns

\n", "
" ], "text/plain": [ " id title publication \\\n", "0 17283 House Republicans Fret About Winning Their Hea... New York Times \n", "1 17284 Rift Between Officers and Residents as Killing... New York Times \n", "2 17285 Tyrus Wong, ‘Bambi’ Artist Thwarted by Racial ... New York Times \n", "3 17286 Among Deaths in 2016, a Heavy Toll in Pop Musi... New York Times \n", "4 17287 Kim Jong-un Says North Korea Is Preparing to T... New York Times \n", ".. ... ... ... \n", "995 18412 Russians Implicated in Doping Still Compete, A... New York Times \n", "996 18413 Mother Jones Is Named Magazine of the Year - T... New York Times \n", "997 18414 Florida Woman Whose ‘Stand Your Ground’ Defens... New York Times \n", "998 18416 First Amendment Support Climbing Among High Sc... New York Times \n", "999 18417 Damien Chazelle, ‘La La Land’ Director, on Cal... New York Times \n", "\n", " content \n", "0 WASHINGTON — Congressional Republicans have... \n", "1 After the bullet shells get counted, the blood... \n", "2 When Walt Disney’s “Bambi” opened in 1942, cri... \n", "3 Death may be the great equalizer, but it isn’t... \n", "4 SEOUL, South Korea — North Korea’s leader, ... \n", ".. ... \n", "995 Many of the world’s winter athletes, now prepa... \n", "996 Mother Jones was named magazine of the year on... \n", "997 Marissa Alexander no longer wears an ankle mon... \n", "998 Support among American high school students fo... \n", "999 Damien Chazelle, the writer and director of th... \n", "\n", "[1000 rows x 4 columns]" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "articles" ] }, { "cell_type": "markdown", "id": "e8686727", "metadata": {}, "source": [ "## TD-IDF 생성하기\n", "\n", "TF-IDF는 Corpus 내에서 발생하는 토큰의 수를 줄이도록 설계되었습니다. TfidfVectorizer 를 사용하면 “documents” 라고도 하는 전체 뉴스 기사 세트에서 어휘가 생성됩니다.\n", "\n", "문서를 가져온 후 Scikit-Learn에서 TFIDFVectorizer를 정의하고 모든 기사의 내용에 대해 실행합니다. 사용되는 매개 변수는 아래에 설명되어 있으며 [scikit-Learn 설명서](https://scikit-learn.org/stable/modules/generated/sklearn.feature_extraction.text.TfidfVectorizer.html)에서 자세히 확인할 수 있습니다.\n", "\n", "* analyzer — 피처가 단어 또는 n-그램 문자로 구성되어야 하는지 여부를 지정합니다.\n", "* ngram_range — ngram 을 생성하기 위한 경계를 정의하며 min_n <= n <= max_n 으로 읽습니다. 이 노트북의 경우 함께 발생할 수 있는 최대 단어는 3,“trigram” 입니다. 만약, 두 단어만 함께 나오는 것을 찾고 있다면 범위를 (1,2) 로 지정하면 “bigram”이 생성됩니다.\n", "* min_df — 문서 빈도가 지정된 임계치보다 낮은 단어를 무시하기 위한 cutoff 값입니다. 이 노트북에서는 문서의 20% 미만에서 단어가 나오는 경우 해당 단어가 제거됩니다. 값이 클수록 보다 aggressive 한 텍스트 필터링이 생성됩니다.\n", "* stop_words — 문제의 맥락에서 가치가 없는 것으로 간주되는 단어입니다. 영어에서 가장 일반적인 단어는 “I”, “me”, “the”, “and”등과 같은 단어를 포함하며, 이 노트북에서는 영어로 된 일반적인 중지 단어를 필터링합니다. 특정 문제에 대한 사용자 지정 단어 목록을 만들 수도 있습니다.\n", "\n", "fit_transform 함수는 NumPy 희소 행렬 (Sparse Matrix) 을 반환합니다. 즉, 이 유형의 행렬에서 대부분의 요소는 0입니다. 요소의 대부분이 0이 아니면 dense 행렬로 간주됩니다. " ] }, { "cell_type": "code", "execution_count": 13, "id": "b36bdd1e", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "CPU times: user 2.25 s, sys: 144 ms, total: 2.39 s\n", "Wall time: 2.39 s\n" ] } ], "source": [ "%%time\n", "\n", "\n", "tf = TfidfVectorizer(analyzer=\"word\", ngram_range=(1, 3),min_df=0.2, stop_words=\"english\")\n", "\n", "tfidf_matrix = tf.fit_transform(articles[\"content\"])" ] }, { "cell_type": "code", "execution_count": 32, "id": "d79df46f", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "<1000x189 sparse matrix of type ''\n", "\twith 59338 stored elements in Compressed Sparse Row format>" ] }, "execution_count": 32, "metadata": {}, "output_type": "execute_result" } ], "source": [ "tfidf_matrix" ] }, { "cell_type": "markdown", "id": "ddde084c", "metadata": {}, "source": [ "## Create recommnedation with MXNet\n", "\n", "MXNet은 고도로 최적화되어 Machine Learning 과 관련된 수학적 계산에 적합합니다. 또한, MXNet은 NumPy보다 빠르게 데이터를 처리 할 수 있습니다.\n", "\n", "잠시 NumPy와 MXNet의 속도를 비교해 보겠습니다." ] }, { "cell_type": "code", "execution_count": 55, "id": "7e6a988c", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "68.8 ms ± 56.7 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)\n" ] } ], "source": [ "%timeit np.dot(tfidf_matrix, tfidf_matrix.T)" ] }, { "cell_type": "code", "execution_count": 34, "id": "2c45b266", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "CPU times: user 5.75 ms, sys: 0 ns, total: 5.75 ms\n", "Wall time: 4.69 ms\n" ] } ], "source": [ "%%time\n", "\n", "mx_tfidf = mx.nd.sparse.array(tfidf_matrix, ctx=mx.cpu())" ] }, { "cell_type": "code", "execution_count": 52, "id": "f5135afe", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "3.19 ms ± 195 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n" ] } ], "source": [ "%%timeit\n", "\n", "mx.nd.sparse.dot(mx_tfidf, mx_tfidf.T)\n", "mx.nd.waitall()" ] }, { "cell_type": "markdown", "id": "aaa5a9c1", "metadata": {}, "source": [ "위 결과에서 알수 있듯이 numpy 를 사용할 때 보다 mxnet 을 사용할 때 더 좋은 성능을 확인할 수 있습니다.\n", "\n", "note: 실험 결과는 SageMaker 의 노트북 instance type 과 환경에 따라 다소 달라질 수 있습니다." ] }, { "cell_type": "markdown", "id": "54604d91", "metadata": {}, "source": [ "## Recommendation" ] }, { "cell_type": "markdown", "id": "60482050", "metadata": {}, "source": [ "cosine similarity 행렬이 생성되면 이제 사용자에게 추천을 할 수 있습니다. 이 노트북에서는 content 에서 만든 추천을 제공하지만 title 에서 권장 사항을 기반으로 합니다. 먼저 콘솔에 처음 10개의 title 을 인쇄합니다." ] }, { "cell_type": "code", "execution_count": 56, "id": "4961313e", "metadata": {}, "outputs": [], "source": [ "def get_recommendations(df_articles, article_idx, mx_mat, n_recs=10):\n", " \"\"\"\n", " Request top N article recommendations.\n", "\n", " INPUT\n", " df_articles: Pandas DataFrame containing all articles.\n", " user_id: User ID being provided matches.\n", " mx_mat: MXNet cosine similarity matrix\n", " OUTPUT\n", " Pandas DataFrame of top N article recommendations.\n", " \"\"\"\n", "\n", " # Similarity and recommendations\n", " article_sims = mx_mat[article_idx].asnumpy()\n", " article_recs = np.argsort(-article_sims).tolist()[:n_recs + 1]\n", "\n", " # Top recommendations\n", " df_recs = df_articles.iloc[article_recs]\n", " df_recs[\"similarity\"] = article_sims[article_recs]\n", "\n", " return df_recs" ] }, { "cell_type": "code", "execution_count": 26, "id": "e40b979c", "metadata": {}, "outputs": [], "source": [ "mx_recsys = mx.nd.sparse.dot(mx_tfidf, mx_tfidf.T)" ] }, { "cell_type": "code", "execution_count": 28, "id": "25a2f2d3", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/home/ec2-user/anaconda3/envs/amazonei_mxnet_p36/lib/python3.6/site-packages/ipykernel/__main__.py:19: SettingWithCopyWarning: \n", "A value is trying to be set on a copy of a slice from a DataFrame.\n", "Try using .loc[row_indexer,col_indexer] = value instead\n", "\n", "See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n" ] } ], "source": [ "df_recs = get_recommendations(df_articles = articles,\n", " article_idx = 3, mx_mat = mx_recsys, n_recs=10)" ] }, { "cell_type": "code", "execution_count": 30, "id": "37c1eb45", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
idtitlepublicationcontentsimilarity
317286Among Deaths in 2016, a Heavy Toll in Pop Musi...New York TimesDeath may be the great equalizer, but it isn’t...1.000000
6517360My Canada - The New York TimesNew York TimesCanada, our No. 1 pick for this year’s 52 Plac...0.595626
65818023Love and Black Lives, in Pictures Found on a B...New York TimesOne night six years ago, on a quiet side stree...0.540853
74018134Postcard From My Past: Crossing Into Syria - T...New York TimesSeventeen years ago, the Middle East was a mor...0.518828
94118354Review: ‘Lincoln in the Bardo’ Shows a Preside...New York TimesLINCOLN IN THE BARDOBy George Saunders343 page...0.513843
23917553Danielle Brooks: The First Time I Saw Myself o...New York TimesIt was June 2012, and I had just sped out of t...0.511749
27017588Neanderthals Were People, Too - The New York T...New York TimesJoachim Neander was a Calvinist theologian w...0.509455
46917812‘I’m Nobody’? Not a Chance, Emily Dickinson - ...New York Times“In the Trumpian sense of the term, she’s the ...0.503491
20617518Is Edward Snowden a Spy? A New Book Calls Him ...New York TimesHOW AMERICA LOST ITS SECRETSEdward Snowden, th...0.494410
19617506Here’s How Clemson Won the College Football Pl...New York TimesClemson upset Alabama and won the college foot...0.479991
26017577President Obama’s Farewell Address: Full Video...New York TimesPresident Obama delivered his farewell address...0.461906
\n", "
" ], "text/plain": [ " id title publication \\\n", "3 17286 Among Deaths in 2016, a Heavy Toll in Pop Musi... New York Times \n", "65 17360 My Canada - The New York Times New York Times \n", "658 18023 Love and Black Lives, in Pictures Found on a B... New York Times \n", "740 18134 Postcard From My Past: Crossing Into Syria - T... New York Times \n", "941 18354 Review: ‘Lincoln in the Bardo’ Shows a Preside... New York Times \n", "239 17553 Danielle Brooks: The First Time I Saw Myself o... New York Times \n", "270 17588 Neanderthals Were People, Too - The New York T... New York Times \n", "469 17812 ‘I’m Nobody’? Not a Chance, Emily Dickinson - ... New York Times \n", "206 17518 Is Edward Snowden a Spy? A New Book Calls Him ... New York Times \n", "196 17506 Here’s How Clemson Won the College Football Pl... New York Times \n", "260 17577 President Obama’s Farewell Address: Full Video... New York Times \n", "\n", " content similarity \n", "3 Death may be the great equalizer, but it isn’t... 1.000000 \n", "65 Canada, our No. 1 pick for this year’s 52 Plac... 0.595626 \n", "658 One night six years ago, on a quiet side stree... 0.540853 \n", "740 Seventeen years ago, the Middle East was a mor... 0.518828 \n", "941 LINCOLN IN THE BARDOBy George Saunders343 page... 0.513843 \n", "239 It was June 2012, and I had just sped out of t... 0.511749 \n", "270 Joachim Neander was a Calvinist theologian w... 0.509455 \n", "469 “In the Trumpian sense of the term, she’s the ... 0.503491 \n", "206 HOW AMERICA LOST ITS SECRETSEdward Snowden, th... 0.494410 \n", "196 Clemson upset Alabama and won the college foot... 0.479991 \n", "260 President Obama delivered his farewell address... 0.461906 " ] }, "execution_count": 30, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df_recs" ] }, { "cell_type": "code", "execution_count": null, "id": "cabc48ec", "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "conda_amazonei_mxnet_p36", "language": "python", "name": "conda_amazonei_mxnet_p36" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.13" } }, "nbformat": 4, "nbformat_minor": 5 }