프로젝트

(프로젝트)딥러닝_영화 추천 서비스 구현_DB연동_MySQL_Pymysql

하방주인장 2023. 7. 1. 22:13

목차

     

    지난 번 포스팅에서 설명했듯이, 이번 프로젝트에서는 새로운 유저가 서비스에 등록했을 때, 유저가 몇몇 영화를 평가하면 이를 기반으로 영화를 추천해주는 서비스를 구현 중이다. 때문에, 유저 정보와 유저의 평가 정보가 등록 될 때 마다 해당 데이터를 저장해야 한다. 그래서, 이번 포스팅에서는 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()

    users
    ratings
    movies

     

    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