<jupyter_start><jupyter_text>Module 1. Personalize User-Item Interaction Data 준비 및 환경 설정MovieLens 데이터셋에서 수집된 데이터를 기반으로, 영화에 대한 추천 모델을 작성하는 법을 안내합니다. 목표는 특정 사용자를 기반으로 하는 영화를 추천하는 것입니다.이 노트북에서는 전체적으로 데이타를 준비하는 단계입니다. 그러기 위해서 아래와 같은 작업을 수행 합니다. * S3 버킷 설정, 데이타 다운로드 및 S3에 데이타 업로드* 데이타 스키마 생성* 데이타 셋 그룹 생성 (DatasetGroup)* 데이타 셋 생성 (Dataset)* 환경 설정 (S3 버킷에 정책(Policy) 부여, Personalize 역할(Role) 생성)* 데이타 Import (S3 --> 데이타 셋)이 노트북을 모두 실행하는데 걸리는 시간은 약 20 ~ 30 분 소요 됩니다. Notebook 사용법코드는 여러 코드 셀들로 구성됩니다. 이 페이지의 상단에 삼각형으로 된 실행 단추를 마우스로 클릭하여 각 셀을 실행하고 다음 셀로 이동할 수 있습니다. 또는 셀에서 키보드 단축키 `Shift + Enter`를 눌러 셀을 실행하고 다음 셀로 이동할 수도 있습니다.셀이 실행되면 셀이 실행되는 동안 측면에 줄이 * 표시되어 있거나 셀 내의 모든 코드를 예측한 후 실행이 완료된 마지막 셀을 나타내기 위해 숫자로 업데이트됩니다.아래 지침을 따르고 셀을 실행하여 Amazon Personalize를 시작하세요.  라이브러리 임포트파이썬에는 광범위한 라이브러리 모음이 포함되어 있으며, 본 핸즈온을 위해서 핵심 데이터 과학 도구인 boto3 (AWS SDK) 및 Pandas/Numpy와 같은 라이브러리를 가져와야 합니다.<jupyter_code># Imports
import boto3
import json
import numpy as np
import pandas as pd
import time<jupyter_output><empty_output><jupyter_text>다음으로 여러분의 환경이 Amazon Personalize와 성공적으로 통신할 수 있는지 확인해야 합니다.<jupyter_code># Configure the SDK to Personalize:
personalize = boto3.client('personalize')
personalize_runtime = boto3.client('personalize-runtime')<jupyter_output><empty_output><jupyter_text>생성할 오브젝트의 끝에 임의의 숫자를 부여하기 위해 suffix 정의<jupyter_code>suffix = str(np.random.uniform())[4:9]<jupyter_output><empty_output><jupyter_text>데이터 설정Amazon S3를 통해 Amazon Personalize로 데이터를 가져옵니다. 아래에서는 이 핸즈온을 위해 AWS에서 생성한 버킷을 지정합니다.아래에서 `bucket` 변수를 업데이트하여 CloudFormation 단계에서 앞서 생성한 값으로 설정해 주세요. 이는 이전 작업의 텍스트 파일에 있어야 합니다.(CloudFormation을 사용한 경우).`filename`은 변경할 필요가 없습니다.  S3 버킷 및 데이터 출력 위치 지정<jupyter_code>import sagemaker
bucket = sagemaker.Session().default_bucket()  # replace with the name of your S3 bucket
filename = "movie-lens-100k.csv"<jupyter_output><empty_output><jupyter_text>훈련 데이터 다운로드, 준비 및 업로드핸즈온을 수행할 MovieLens 데이터가 아직 로컬로 로드되어 있지 않은 경우 아래 줄을 실행하여 최신 사본을 다운로드하고 신속하게 확인해 보세요.  데이터셋 다운로드 및 탐색<jupyter_code>!wget -N http://files.grouplens.org/datasets/movielens/ml-100k.zip
!unzip -o ml-100k.zip
data = pd.read_csv('./ml-100k/u.data', sep='\t', names=['USER_ID', 'ITEM_ID', 'RATING', 'TIMESTAMP'])
pd.set_option('display.max_rows', 5)
data<jupyter_output>--2020-03-17 23:44:13--  http://files.grouplens.org/datasets/movielens/ml-100k.zip
Resolving files.grouplens.org (files.grouplens.org)... 172.16.17.32
Connecting to files.grouplens.org (files.grouplens.org)|172.16.17.32|:80... connected.
HTTP request sent, awaiting response... 304 Not Modified
File ‘ml-100k.zip’ not modified on server. Omitting download.

Archive:  ml-100k.zip
  inflating: ml-100k/allbut.pl       
  inflating: ml-100k/mku.sh          
  inflating: ml-100k/README          
  inflating: ml-100k/u.data          
  inflating: ml-100k/u.genre         
  inflating: ml-100k/u.info          
  inflating: ml-100k/u.item          
  inflating: ml-100k/u.occupation    
  inflating: ml-100k/u.user          
  inflating: ml-100k/u1.base         
  inflating: ml-100k/u1.test         
  inflating: ml-100k/u2.base         
  inflating: ml-100k/u2.test         
  inflating: ml-100k/u3.base         
  inflating: ml-100k/u3.test         
  inflating: ml-100k/u4.base         
  inflat[...]<jupyter_text>데이터 준비 및 업로드이 데이터에는 UserID, ItemID, Rating 및 Timestamp 컬럼이 포함되어 있습니다.이제 순위가 낮은(low ranking) 항목을 제거하고 모델을 만들기 전에 해당 열을 제거합니다.완료되면 파일을 새 CSV로 저장한 다음, S3에 업로드합니다.이 모든 것들은 단순히 아래 코드 셀을 실행하여 수행합니다.<jupyter_code>data = data[data['RATING'] > 3.6]                # keep only movies rated 3.6 and above
data = data[['USER_ID', 'ITEM_ID', 'TIMESTAMP']] # select columns that match the columns in the schema below
data.to_csv(filename, index=False)

boto3.Session().resource('s3').Bucket(bucket).Object(filename).upload_file(filename)<jupyter_output><empty_output><jupyter_text>스키마 생성Personalize가 데이터를 이해하는 방법의 핵심 구성 요소는 아래 정의 된 스키마(schema)에서 비롯됩니다. 이 설정은 CSV 파일을 통해 제공된 데이터를 요약하는 방법을 Personalize 서비스에 알려줍니다. 열(column)과 유형(type)은 위에서 만든 파일의 내용과 일치합니다.<jupyter_code>schema = {
    "type": "record",
    "name": "Interactions",
    "namespace": "com.amazonaws.personalize.schema",
    "fields": [
        {
            "name": "USER_ID",
            "type": "string"
        },
        {
            "name": "ITEM_ID",
            "type": "string"
        },
        {
            "name": "TIMESTAMP",
            "type": "long"
        }
    ],
    "version": "1.0"
}

create_schema_response = personalize.create_schema(
    name = "DEMO-schema" + suffix,
    schema = json.dumps(schema)
)

schema_arn = create_schema_response['schemaArn']
print(json.dumps(create_schema_response, indent=2))<jupyter_output>{
  "schemaArn": "arn:aws:personalize:us-east-2:057716757052:schema/DEMO-schema91814",
  "ResponseMetadata": {
    "RequestId": "3e40c963-ab7f-43a8-9148-6a6aac4182a6",
    "HTTPStatusCode": 200,
    "HTTPHeaders": {
      "content-type": "application/x-amz-json-1.1",
      "date": "Tue, 17 Mar 2020 23:44:16 GMT",
      "x-amzn-requestid": "3e40c963-ab7f-43a8-9148-6a6aac4182a6",
      "content-length": "82",
      "connection": "keep-alive"
    },
    "RetryAttempts": 0
  }
}<jupyter_text>데이터셋 그룹 생성 및 대기Personalize에서 가장 큰 단위는 **데이터셋 그룹(Dataset Group)** 이며, 이렇게 하면 데이터, 이벤트 추적기(event tracker), 솔루션(solution) 및 캠페인(campaign)이 분리됩니다. 공통의 데이터 수집을 공유하는 것들을 그룹화합니다. 원하는 경우 아래 그룹명을 자유롭게 변경해 주세요.  데이터셋 그룹 생성<jupyter_code>create_dataset_group_response = personalize.create_dataset_group(
    name = "DEMO-dataset-group" + suffix
)

dataset_group_arn = create_dataset_group_response['datasetGroupArn']
print(json.dumps(create_dataset_group_response, indent=2))<jupyter_output>{
  "datasetGroupArn": "arn:aws:personalize:us-east-2:057716757052:dataset-group/DEMO-dataset-group91814",
  "ResponseMetadata": {
    "RequestId": "b10c638d-5a93-4d6b-9a6b-0bb5a820cc20",
    "HTTPStatusCode": 200,
    "HTTPHeaders": {
      "content-type": "application/x-amz-json-1.1",
      "date": "Tue, 17 Mar 2020 23:44:17 GMT",
      "x-amzn-requestid": "b10c638d-5a93-4d6b-9a6b-0bb5a820cc20",
      "content-length": "102",
      "connection": "keep-alive"
    },
    "RetryAttempts": 0
  }
}<jupyter_text>데이터셋 그룹이 활성화 상태가 될 때까지 대기아래의 모든 항목에서 Dataset Group을 사용하려면 활성화(active)가 되어야 합니다. 아래 셀을 실행하고 DatasetGroup: ACTIVE로 변경될 때까지 기다려 주세요.<jupyter_code>max_time = time.time() + 3*60*60 # 3 hours
while time.time() < max_time:
    describe_dataset_group_response = personalize.describe_dataset_group(
        datasetGroupArn = dataset_group_arn
    )
    status = describe_dataset_group_response["datasetGroup"]["status"]
    print("DatasetGroup: {}".format(status))
    
    if status == "ACTIVE" or status == "CREATE FAILED":
        break
        
    time.sleep(60)<jupyter_output>DatasetGroup: CREATE PENDING
DatasetGroup: ACTIVE<jupyter_text>데이터셋 생성그룹 다음으로 생성할 것은 실제 데이터셋입니다. 이 예에서는 1개의 interaction 데이터만 작성합니다. 아래의 코드 셀을 실행하여 데이터셋을 생성해 주세요.<jupyter_code>dataset_type = "INTERACTIONS"
create_dataset_response = personalize.create_dataset(
    name = "DEMO-dataset" + suffix,
    datasetType = dataset_type,
    datasetGroupArn = dataset_group_arn,
    schemaArn = schema_arn
)

dataset_arn = create_dataset_response['datasetArn']
print(json.dumps(create_dataset_response, indent=2))<jupyter_output>{
  "datasetArn": "arn:aws:personalize:us-east-2:057716757052:dataset/DEMO-dataset-group91814/INTERACTIONS",
  "ResponseMetadata": {
    "RequestId": "0ab0e243-f89c-4970-bf5f-f29880f35400",
    "HTTPStatusCode": 200,
    "HTTPHeaders": {
      "content-type": "application/x-amz-json-1.1",
      "date": "Tue, 17 Mar 2020 23:45:17 GMT",
      "x-amzn-requestid": "0ab0e243-f89c-4970-bf5f-f29880f35400",
      "content-length": "104",
      "connection": "keep-alive"
    },
    "RetryAttempts": 0
  }
}<jupyter_text>S3 버킷에 정책 부여Amazon Personalize는 앞서 생성한 S3 버킷의 내용을 읽을 수 있어야 합니다. 아래 코드 셀로 S3 버킷 접근 정책(policy)을 부여합니다.<jupyter_code>s3 = boto3.client("s3")

policy = {
    "Version": "2012-10-17",
    "Id": "PersonalizeS3BucketAccessPolicy",
    "Statement": [
        {
            "Sid": "PersonalizeS3BucketAccessPolicy",
            "Effect": "Allow",
            "Principal": {
                "Service": "personalize.amazonaws.com"
            },
            "Action": [
                "s3:GetObject",
                "s3:ListBucket"
            ],
            "Resource": [
                "arn:aws:s3:::{}".format(bucket),
                "arn:aws:s3:::{}/*".format(bucket)
            ]
        }
    ]
}

s3.put_bucket_policy(Bucket=bucket, Policy=json.dumps(policy))<jupyter_output><empty_output><jupyter_text>Personalize 역할 생성또한, Amazon Personalize는 특정 작업들을 실행할 권한을 갖기 위해, AWS에서 역할을 맡을 수 있는 기능이 필요합니다.<jupyter_code>iam = boto3.client("iam")

role_name = "PersonalizeRoleDemo" + suffix
assume_role_policy_document = {
    "Version": "2012-10-17",
    "Statement": [
        {
          "Effect": "Allow",
          "Principal": {
            "Service": "personalize.amazonaws.com"
          },
          "Action": "sts:AssumeRole"
        }
    ]
}

create_role_response = iam.create_role(
    RoleName = role_name,
    AssumeRolePolicyDocument = json.dumps(assume_role_policy_document)
)

# AmazonPersonalizeFullAccess provides access to any S3 bucket with a name that includes "personalize" or "Personalize" 
# if you would like to use a bucket with a different name, please consider creating and attaching a new policy
# that provides read access to your bucket or attaching the AmazonS3ReadOnlyAccess policy to the role
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonPersonalizeFullAccess"
iam.attach_role_policy(
    RoleName = role_name,
    PolicyArn = policy_arn
)

# Now add S3 support
iam.attach_role_policy(
    RoleName=role_name,    
    PolicyArn='arn:aws:iam::aws:policy/AmazonS3FullAccess'
)
time.sleep(60) # wait for a minute to allow IAM role policy attachment to propagate

role_arn = create_role_response["Role"]["Arn"]
print(role_arn)<jupyter_output>arn:aws:iam::057716757052:role/PersonalizeRoleDemo91814<jupyter_text>데이터 Import이전에는 정보를 저장하기 위해 데이터셋 그룹 및 데이터셋을 생성했으므로, 이제는 모델 구축을 위해 S3에서 Amazon Personalize로 데이터를 로드하는 import job을 실행합니다. 데이터셋 Import Job 생성<jupyter_code>create_dataset_import_job_response = personalize.create_dataset_import_job(
    jobName = "personalize-demo-import1" + suffix,
    datasetArn = dataset_arn,
    dataSource = {
        "dataLocation": "s3://{}/{}".format(bucket, filename)
    },
    roleArn = role_arn
)

dataset_import_job_arn = create_dataset_import_job_response['datasetImportJobArn']
print(json.dumps(create_dataset_import_job_response, indent=2))<jupyter_output>{
  "datasetImportJobArn": "arn:aws:personalize:us-east-2:057716757052:dataset-import-job/personalize-demo-import191814",
  "ResponseMetadata": {
    "RequestId": "211e2028-4a5a-499e-802d-f9ca8cd57351",
    "HTTPStatusCode": 200,
    "HTTPHeaders": {
      "content-type": "application/x-amz-json-1.1",
      "date": "Tue, 17 Mar 2020 23:46:18 GMT",
      "x-amzn-requestid": "211e2028-4a5a-499e-802d-f9ca8cd57351",
      "content-length": "117",
      "connection": "keep-alive"
    },
    "RetryAttempts": 0
  }
}<jupyter_text>데이터셋 Import job이 활성화 상태가 될 때까지 대기Import job이 완료되기까지 시간이 걸립니다. 아래 코드 셀의 출력 결과가 DatasetImportJob: ACTIVE가 될 때까지 기다려 주세요.<jupyter_code>%%time

max_time = time.time() + 3*60*60 # 3 hours
while time.time() < max_time:
    describe_dataset_import_job_response = personalize.describe_dataset_import_job(
        datasetImportJobArn = dataset_import_job_arn
    )
    status = describe_dataset_import_job_response["datasetImportJob"]['status']
    print("DatasetImportJob: {}".format(status))
    
    if status == "ACTIVE" or status == "CREATE FAILED":
        break
        
    time.sleep(60)<jupyter_output>CPU times: user 2 µs, sys: 0 ns, total: 2 µs
Wall time: 4.53 µs
DatasetImportJob: CREATE PENDING
DatasetImportJob: CREATE IN_PROGRESS
DatasetImportJob: CREATE IN_PROGRESS
DatasetImportJob: CREATE IN_PROGRESS
DatasetImportJob: CREATE IN_PROGRESS
DatasetImportJob: CREATE IN_PROGRESS
DatasetImportJob: CREATE IN_PROGRESS
DatasetImportJob: CREATE IN_PROGRESS
DatasetImportJob: CREATE IN_PROGRESS
DatasetImportJob: CREATE IN_PROGRESS
DatasetImportJob: CREATE IN_PROGRESS
DatasetImportJob: CREATE IN_PROGRESS
DatasetImportJob: ACTIVE<jupyter_text>리뷰위의 코드를 사용하여 데이타셋 그룹, 데이타셋, 데이타셋 Import까지를 수행 하였습니다. 다음 단계는 이를 기반으로 솔류션(모델)을 생성하는 단계를 진행 합니다..이제 다음 노트북으로 넘어갈 준비가 되었습니다. (`2.Creating_and_Evaluating_Solutions.ipynb`) 다음 노트북에 대한 참고 사항다음 실습에 필요한 몇 가지 값들이 있습니다. 아래 셀을 실행하여 저장한 후, 다음 주피터 노트북에서 그대로 사용할 수 있습니다.<jupyter_code>%store data
%store dataset_group_arn
%store dataset_arn
%store schema_arn
%store bucket
%store filename
%store role_name<jupyter_output>Stored 'data' (DataFrame)
Stored 'dataset_group_arn' (str)
Stored 'dataset_arn' (str)
Stored 'schema_arn' (str)
Stored 'bucket' (str)
Stored 'filename' (str)
Stored 'role_name' (str)
{'fraction_non_alphanumeric': 0.11454191033138401, 'fraction_numerical': 0.04280701754385965, 'mean_word_length': 3.816372512204281, 'pattern_counts': {'":': 73, '<': 54, '<?xml version=': 0, '>': 54, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 2, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 1, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '15', 'dir': 'jupyter-structured-clean-dedup', 'id': '153797', 'license': 'MIT', 'n_tokens_mistral': 6415, 'n_tokens_neox': 6936, 'n_words': 1123, 'path': '1.Validating_and_Importing_User_Item_Interaction_Data.ipynb', 'repo_name': 'gonsoomoon-ml/recommendation-workshop', 'size': '12829'}