[iOS] CoreML로 추천시스템 구현하기(2)

Youth·2023년 6월 19일
1

TIL

목록 보기
5/20
post-thumbnail
post-custom-banner

CoreML로 간단한 앱페이지 만들어보기

저번 TIL에서 만들었던 추천모델을 이용해서 실제로 유저의 interaction을 바탕으로 추천 아티클을 유저에게 보여주는 뷰를 구현해봤다. 사실 이걸 구현하려고 하기전에 아무래도 아무런 사전지식이 없어서 구글링을 많이 해봤는데 생각보다 레퍼런스가 많이 없어서 당황했다. 다른 coreML, 뭐 image detection이나 classification은 정보가 되게 많았는데 recommendation은 레퍼런스가 너무 없어서 놀라긴 했다. 그래서 정말 몇개없는 레퍼런스를 바탕으로 구현해본 기록이라고 생각해주면 좋을거같다.

우선 coreML은 기본적으로 실시간으로 모델을 업데이트하는 방식은 아닌듯 싶다. 그래서 보통 일정 데이터를 가지고 모델을 만들고 그 모델을 프로젝트에 넣어놓고 그 모델을 가지고 매번 예측을 해준다. 그러다보니 앱을 릴리즈하고나서 일정시간마다 새로 업데이트 되는 데이터로 모델을 다시 만들어서 넣어야하는게 아닐까라는 생각이 들었다.

1.모델 넣어서 객체 만들기

인터넷이 돌아다니는 레퍼런스를 보면 아얘 model자체를 드래그해서 프로젝트에 넣어준다

그리고 나서 해당 모델 객체를 생성해줘야하는데

private let exapmleRecommendationModel = ArticleRecommendationModel()

보통 다 이렇게 선언을 해서 나도 그냥 이렇게 선언을 했고 예측하는데는 아무런 문제가 없었다(사실 문제가 없는지는 모르겠고 돌아가긴한다) 근데 문제는 거슬리는 경고가 뜬다

그래서 구글링을 해보니 이제 configuration이라는 parameter에 MLModelConfiguration객체를 만들어서 넣어야 혹여나 이 config모델을 만들지 못했을때(모델을 찾지 못하는 문제가 발생했을때) error handling이 가능해진다고 한다

private var recommendationModel: ArticleRecommendationModel = {
    do {
        let config = MLModelConfiguration()
        return try ArticleRecommendationModel(configuration: config)
    } catch {
        fatalError("추천모델을 찾지 못했습니다")
    }
}()

이런식으로 모델이 init되지 않는다면 fatalError가 발생하도록 분기처리를 해주면 해당 경고가 발생하지 않는다


2.input에 대한 예측 결과 받아오기

기본적인 코드의 흐름을 적어보면 recommendationData가 있고 거기에 유저의 input(어떤 아티클에 좋아요를 눌렀는지)가 들어오면 그때마다 모델에서 추천 아티클을 suggestionArticleList에 넣어주고 그때마다 view의 collectionView가 reload되는 방식으로 코드를 구성했다.

그렇다면 이렇게 총 3가지의 방법을 나눠서 살펴보자

1.recommendationData에 user의 input넣기

func storeUserLikeDate(key: String, value: Double) {
    self.recommenationData[key] = value
}

한가지 명심해야할 부분이 있다면 기본적으로 CoreML recommendation Model은 key와 value로 이루어진 데이터쌍으로 구성되어있다 여기서는 아티클이름이 key가 되고 해당 아티클에 좋아요를 눌렀는지 여부가 value에 들어가있다.

그래서 내가 어떤 cell의(cell에는 좋아요 버튼과 아티클이름이 있다) 아티클에 좋아요를 누르면 위의 함수가 호출된다.

extension ViewController: ArticleLikeDelegate {
    func changeArticleLike(sender: UIButton, isLike: Bool) {
        guard let indexPath = myArticleTableView.indexPathForRow(at: sender.convert(CGPoint.zero, to: myArticleTableView)) else { return }
        var selectedData = viewModel.data[indexPath.row]
        selectedData.articelLike = isLike
        viewModel.storeUserLikeDate(key: selectedData.articelName, value: selectedData.articelLike ? 1.0 : 0.0)
    }
}

이렇게 내가 누른 버튼의 indexPath를 delegate로 받아와서 해당 데이터를 viewModel의 함수의 input으로 넣어주는 방식이다.

2.예측 데이터를 받아오기

private var recommenationData: [String : Double] = [:] {
    didSet {
        let input = ArticleRecommendationModelInput(items: recommenationData, k: 5)
        guard let unwrappedResults = try? recommendationModel.prediction(input: input) else {
            fatalError("Could not get results back!")
        }
        suggestionArticleList = unwrappedResults.recommendations
    }
}

1번을 통해 recommendationData가 변하게되면 해당모델의 input객체를 생성하는데 그떄 input으로 해당 dictionary를 넣어주고 k를 넣어준다 여기서 k는 한번에 받아올 데이터의 갯수를 의미한다. 나는 최대 5개로 선언을 해놓은 상황이다. 하지만 말그대로 input객체일뿐 내가 모델에 넣어야할 데이터를 만들어준거에 불과하다.

그리고나서 우리가 만든 모델객체에 예측 메서드에다가 input객체를 넣어주는데 당연히 return이 옵셔널일거다 return값이 없을수도 있으니까? 그래서 guard문을 넘어왔을때 비로소 예측 모델을 suggestionArticleList에 넣어준다.


3.결과

그러면 내가 좋아요를 누를 때마다 input객체가 생성되고 그 객체를 예측객체의 input으로 넣어서 옵셔널 바인딩을 통해 값이 오지않을 상황을 걸러주고 들어온 객체를 user에게 collectionView형태로 보여주면 결과 영상처럼 유저의 좋아요에따라 추천아티클이 실시간으로 변하는 coreML Recommendation 화면을 만들어낼 수 있게된다

profile
AppleDeveloperAcademy@POSTECH 1기 수료, SOPT 32기 iOS파트 수료
post-custom-banner

0개의 댓글