(프로젝트)딥러닝_영화 추천 서비스 구현_DB연동_MySQL_Pymysql
목차
지난 번 포스팅에서 설명했듯이, 이번 프로젝트에서는 새로운 유저가 서비스에 등록했을 때, 유저가 몇몇 영화를 평가하면 이를 기반으로 영화를 추천해주는 서비스를 구현 중이다. 때문에, 유저 정보와 유저의 평가 정보가 등록 될 때 마다 해당 데이터를 저장해야 한다. 그래서, 이번 포스팅에서는 pymysql을 사용하여 DB와 연동하여 MovieLens 데이터셋을 DB에 저장해보려고 한다.
1. Data Load: MovieLens Data Set
먼저, MovieLens 데이터셋을 다운받아 전처리 후 csv 형태로 저장하려고 한다.
MovieLens 데이터셋은 유저의 정보가 담긴 users, 유저별 영화 평점 정보가 담긴 ratings, 영화 정보가 담긴 movies 로 구성되어 있다.
import pandas as pd
# Download MovieLens data.
print("Downloading movielens data...")
from urllib.request import urlretrieve
import zipfile
urlretrieve("http://files.grouplens.org/datasets/movielens/ml-100k.zip", "movielens.zip")
zip_ref = zipfile.ZipFile('movielens.zip', "r")
zip_ref.extractall()
print("Done. Dataset contains:")
print(zip_ref.read('ml-100k/u.info'))
# user data
users_cols = ['user_id', 'age', 'sex', 'occupation', 'zip_code']
users = pd.read_csv(
'ml-100k/u.user', sep='|', names=users_cols, encoding='latin-1')
users.head()
# ratings data
ratings_cols = ['user_id', 'movie_id', 'rating', 'unix_timestamp']
ratings = pd.read_csv(
'ml-100k/u.data', sep='\t', names=ratings_cols, encoding='latin-1')
ratings.head()
# movie data
genre_cols = [
"genre_unknown", "Action", "Adventure", "Animation", "Children", "Comedy",
"Crime", "Documentary", "Drama", "Fantasy", "Film-Noir", "Horror",
"Musical", "Mystery", "Romance", "Sci-Fi", "Thriller", "War", "Western"
]
movies_cols = [
'movie_id', 'title', 'release_date', "video_release_date", "imdb_url"
] + genre_cols
movies = pd.read_csv(
'ml-100k/u.item', sep='|', names=movies_cols, encoding='latin-1')
movies.head()
2. 데이터 전처리
2-1. users
users 테이블은 user_id, age(나이), sex(성별), occupation(직업), zip_code(주소)로 구성되어있다. zip_code 컬럼은 사용성도 없기 때문에 제거하기로 결정했다.
# Null 값 확인
users.info() # Null 값 없음
# zipcode 컬럼 삭제
users.drop(columns='zip_code', inplace=True)
2-2. ratings
ratings 테이블은 user_id, movie_id, rating(점수), unix_timestamp(시간)로 구성되어있다. 이번 프로젝트에서는 유저가 평가를 할 때에 시간을 등록할 수가 없으므로 unix_timestamp 컬럼을 제거하기로 결정했다.
# Null 값 확인
ratings.info() # Null 값 없음
# rating 범위 확인
ratings.describe() # rating: 1~5점
# unix_timestamp 컬럼 삭제
ratings.drop(columns='unix_timestamp', inplace=True)
ratings.head()
2-3. movies
movies 테이블은 movie_id, title(영화 제목), release_date(출시일자), video_release_date, imdb_url 컬럼과 해당 영화가 어떤 장르인지 원 핫 인코딩 형식으로 나타내져 있다. video_release_date 컬럼은 아예 비워져 있기 때문에 삭제하고 각 장르 컬럼들을 합친 all_genre 컬럼을 생성하기로 했다.
# Null 값 확인
movies.info() # video_release_date 컬럼이 비어있음
# video_release_date 컬럼 삭제
movies.drop(columns='video_release_date', inplace=True)
# 각 장르 컬럼들로 all_genre 컬럼 생성
# all_genre 컬럼 생성하는 함수 생성
def get_all_genres(gs):
active = [genre for genre, g in zip(genre_cols, gs) if g==1]
if len(active) == 0:
return 'Other'
return '-'.join(active)
# all genre 컬럼 생성
movies['all_genres'] = [get_all_genres(gs) for gs in zip(*[movies[genre] for genre in genre_cols])]
또한, 현재 ID 들은 1부터 시작하기 때문에 인덱스와 맞춰주기 위해 0부터 시작하는 것으로 바꿔 주어야 한다.
# ID를 0 부터 시작으로 변환
users["user_id"] = users["user_id"].apply(lambda x: str(x-1))
movies["movie_id"] = movies["movie_id"].apply(lambda x: str(x-1))
ratings["movie_id"] = ratings["movie_id"].apply(lambda x: str(x-1))
ratings["user_id"] = ratings["user_id"].apply(lambda x: str(x-1))
ratings["rating"] = ratings["rating"].apply(lambda x: float(x))
이제, 모든 전처리가 끝났으니 3개의 데이터프레임을 합친 movielens 데이터프레임을 생성해보도록 하겠다.
또한, csv 저장 전에 movies 데이터프레임에 있는 각 장르 컬럼은 movielens 데이터프레임에 있기도 하고 저장 공간을 위해 모두 삭제하기로 결정했다.
# Create one merged DataFrame containing all the movielens data.
movielens = ratings.merge(movies, on='movie_id').merge(users, on='user_id')
movielens.head()
# movies genre 컬럼 drop
drop_cols = movies.columns.difference(['movie_id','title','release_date','imdb_url','all_genres'])
movies.drop(columns=drop_cols, inplace=True)
3. csv 저장
# csv 저장
users.to_csv('../data/users.csv', index=False)
ratings.to_csv('../data/ratings.csv', index=False)
movies.to_csv('../data/movies.csv', index=False)
movielens.to_csv('../data/movielens.csv', index=False)
4. Create DB, Table
데이터도 준비되었으니 pymysql로 내 로컬에 있는 MySQL DB와 연동하여 데이터베이스를 생성하고 users, movies, ratings 테이블을 만들어보도록 하자. 각각 users 테이블은 user_id를, movis 테이블은 movie_id를 ratings 테이블은 rating_id 컬럼을 생성하여 해당 컬럼을 Primary Key로 설정하고, ratings 테이블과 users, movies 테이블을 각각의 id 컬럼으로 Foreign Key를 설정하여 참조 관계를 나타내기로 했다.
4-1. Creat DB
import pandas as pd
import pymysql
# mysql 연동
conn = pymysql.connect(host='127.0.0.1'
, port=3306
, user = *****
, password= *****
, charset='utf8'
)
# movie_pj 데이터베이스 생성
cursor = conn.cursor()
cursor.execute("CREATE DATABASE movie_pj")
# commit 후 연결 해제
conn.commit()
conn.close()
4-2. Create Table
conn = pymysql.connect(host='127.0.0.1'
, port=3306
, user = 'root'
, password='root1234'
, db = 'movie_pj'
, charset='utf8'
)
create_table_users = """
create table users (
user_id int not null primary key,
age int,
sex VARCHAR(10),
occupation VARCHAR(100)
);
"""
create_table_movies= """
create table movies (
movie_id int not null primary key,
title VARCHAR(100),
release_date VARCHAR(50),
imdb_url VARCHAR(300),
all_genres VARCHAR(100)
);
"""
create_table_ratings= """
create table ratings (
rating_id int AUTO_INCREMENT primary key not null,
user_id int not null,
movie_id int not null,
rating int,
FOREIGN KEY (user_id) REFERENCES users (user_id),
FOREIGN KEY (movie_id) REFERENCES movies (movie_id)
);
""" # foreign key 참조를 위해 primary key(rating_id)를 새로 생성
cursor = conn.cursor()
cursor.execute(create_table_users)
cursor.execute(create_table_movies)
cursor.execute(create_table_ratings)
5. Insert DB
다음은 앞에서 저장했던 csv 파일을 불러와서 각각 데이터베이스에 있는 데이터를 테이블에 Insert 하는 코드이다.
# insert users table
users = pd.read_csv('../data/users.csv')
for i in range(len(users)):
cursor.execute(f'INSERT INTO users (user_id, age, sex, occupation) VALUES \
({users["user_id"][i]}, {users["age"][i]}, "{users["sex"][i]}", "{users["occupation"][i]}");')
# insert movies table
movies = pd.read_csv('../data/movies.csv')
for i in range(len(movies)):
cursor.execute(f'INSERT INTO movies (movie_id, title, release_date, imdb_url, all_genres) VALUES \
({movies["movie_id"][i]}, "{movies["title"][i]}", "{movies["release_date"][i]}", "{movies["imdb_url"][i]}", "{movies["all_genres"][i]}");')
# insert ratings table
ratings = pd.read_csv('../data/ratings.csv')
for i in range(len(ratings)):
cursor.execute(f"INSERT INTO ratings (user_id, movie_id, rating) VALUES \
({ratings['user_id'][i]}, {ratings['movie_id'][i]}, {ratings['rating'][i]});")
# commit 후 DB 연동 해제
conn.commit()
conn.close()
실제로, MySQL Workbench에 가보면 데이터가 잘 들어왔음을 볼 수 있다. 그렇다면, 다음 포스팅에서는 유저 등록 부터 평점 등록 까지의 서비스를 streamlit을 활용하여 구현해보도록 하겠다.
깃허브에 가시면 모든 코드가 있습니다.
https://github.com/pulpo125/movie_recommendation
GitHub - pulpo125/movie_recommendation
Contribute to pulpo125/movie_recommendation development by creating an account on GitHub.
github.com