이번 프로젝트에서는 간단하지만 새로운 기능을 함께 사용하여 진행해보았다.
iOS 개발의 기능 중 하나인 기계 학습(ML)이다. 모든 iPhone에는 CoreML이라는 기술이 내장되어 있고 이전의 데이터를 통해 새로운 데이터를 예측하는 코드를 작성하게 된다.
앱의 이름은 BetterRest이며, 사용자들에게 3가지 질문을 통해 숙면을 취할 수있는 시간을 추천해주는 방식으로 설계된다.
- 언제 일어나길 원하는가?
- 원하는 수면 시간은 약 몇 시간인가?
- 하루에 몇 잔의 커피를 마시는가?
다음 3가지 질문으로 얻은 답을 이용하여 CoreML에 입력 하면 언제 잠자리에 들어야 하는지 알려주는 결과를 얻습니다. 생각해보면 가능한 답은 수십억 가지가 있습니다. 다양한 기상 시간에 수면 시간을 곱하고 다시 전체 커피 양을 곱한 것이다.
본격적으로 시작해보자!
CoreML.
이번 프로젝트는 특별히 먼저 새로운 기술인 CoreML을 이용하여 기계학습을 한 이후에 기본적인 레이아웃 개발로 진행해보자.
CoreML이 등장한 과정부터 간단히 살펴보자면
iOS11에서 on-device 기계 학습은 CoreML이라는 프레임 워크를 이용하여 등장하였다. 이후 애플은 Create ML이라는 2번째 프레임 워크를 출시했고 그 결과로 누구나 자신의 앱에 머신 러닝 작업을 드래깅 작업으로 편하게 할 수 있게 되었다.
이제 바로 학습을 진행해보자!
먼저 Xcode 메뉴에서 Open Developer Tool > Create ML을 선택하면 된다. 혹시 몰라 참고 사진도 추가하였다.
프로젝트를 선택하고 시작하면 새 문서를 열고 기존의 4 가지 값(언제 일어나고 싶은지, 얼마나 자고 싶은지, 하루에 커피를 얼마나 먹는지 얼마나 많이 자는지)으로 구성된 데이터 BetterRest.csv를 선택하는데 여기서 아래 사진에서 보면 Select 버튼을 눌러 자신의 csv파일을 불러오면 된다. 그리고 아래 Target에선 우리가 원하는 결과인 해당 사용자가 실제로 얼마큼 자야 하는지 예측을 해야 하기 때문에 actualSleep을 선택하였고 이를 예측하기 위해 필요한 요소로 일어나는 때, 원하는 취침시간, 커피 섭취량을 추가한다.
이게 끝나면 알고리즘에 대한 5가지 옵션이 제공된다. 자동, 랜덤 포레스트, 부스트 트리, 의사결정 트리 및 선형 회귀로 여기서는 본인이 원하는 방법을 선택해도 되지만 자동을 선택하는 경우 해당 데이터에 가장 알맞은 옵션을 알아서 찾아서 제공해준다!!🕵️♂️
이후 상당에 있는 Train버튼을 누르면 학습을 시작하게 된다. 데이터의 양이 그렇게 많진 않기 때문에 금방 끝난다.
다 끝난 결과는 바로 Xcode로 들고 가거나 아니면 컴퓨터 디렉터리에 저장할 수 있다.
이렇게 하면 매우 쉽게 하나의 기계학습 과정은 끝이 나고 앱에 필요한 학습 메타데이터들은 모두 가지고 있게 된다. 이제 본격적인 Ui설계로 들어가자!!
Building Basic Layout.
@State private var sleepAmount = 8.0
Stepper("\(sleepAmount) hours", value: $sleepAmount)
먼저 수면의 양을 측정받기 위한 sleepAmount를 정의하고 이를 Stepper를 이용하여 바인딩하여 현재 값을 표시하였다.
그런데 여기서 Stepper란?
증가와 감소 작업, 예를 들면 지금 해당 프로젝트에서 시간을 다루기 때문에 이는 사용자가 이전에 사용하였던 slider보다는 훨씬 편하게 정확한 숫자를 입력할 수 있게 된다.
계속해서 Stepper의 경우 범위의 값을 제한하여 사용자가 허용하는 값을 제한할 수 있다. 예를 들어 최소 수면 시간으로 선택할 수 있는 시간이 최소 4시간에서 최대 12시간이라면 아래와 같이 속성 값만 추가해주면 문제없다.
Stepper("\(sleepAmount.formatted()) hours", value: $sleepAmount, in: 4...12, step: 0.25)
또 마지막에 보면 step이라고 있는데 이는 15분을 간격으로 시간을 선택할 수 있게끔 설정하였다. 따라서 8시간, 8시간 15분, 8시간 30분... 이런 식으로 너무 범위가 넓지도 좁지도 않은 선에서 설정해보았다. formatted는 Double형식으로 지정하여 사용하게끔 요청할 때 사용한다. 만약 사용하지 않으면 8시간이 아닌 8.0000000으로 출력되는 것을 확인할 수 있다.
이어서 날짜 선택기와 두 개의 Stepper를 이용하여 사용자가 언제 일어나고 싶어 하고, 얼마나 자고 싶고, 커피를 얼마나 마셨는지를 입력받을 컨트롤과 그 정보를 저장할 속성을 추가해보자.
@State private var wakeUp = Date.now
@State private var sleepAmount = 8.0
@State private var coffeeAmount = 0
이후 이젠 익숙해진 NavigationView를 이용하여 전체적으로 래핑을 먼저 해주자.
var body: some View {
NavigationView {
Form {
Section {
Text("몇시에 기상하고 싶으신가요?")
.font(.headline)
DatePicker("Please enter a time", selection: $wakeUp, displayedComponents: .hourAndMinute)
.labelsHidden()
Text("몇시간 동안 자고 싶으신가요?")
.font(.headline)
Stepper("\(sleepAmount.formatted()) hours",
value: $sleepAmount, in: 4...12, step: 0.25)
}
이후 참고 영상에서는 VStack을 이용하지만, Section으로 분리하여 사용하는 것이 가독성 측면에서 더욱 좋을 것 같아 Section을 이용해보았다. 그리고 이후 Stepper를 이용하여 앞서 언급하였던 수면 시간에 대한 접근 또한 해당 섹션에 추가하였다.
또 다른 섹션에는 하루 커피 섭취량을 입력받는 Stepper로
Text("하루 카페인(coffe) 섭취량")
.font(.headline)
Stepper(coffeeAmount == 0 ? "0 cup" : "\(coffeeAmount) cups", value: $coffeeAmount, in: 0...20)
사용자가 커피량에 대한 선택을 0으로 한 경우 그대로 0으로 가지지만, 그렇지 않으면 해당 양에 "cups"를 더한 값이 사용되는 삼항 연산자를 이번 프로젝트에서 사용해보았다. 기본적으로 coffeAmount 자체가 0이라고 초기값을 정해 줬기 때문에 당연히 사용자가 Stepper를 선택하기 전에는 "0 cup"이라고 출력된다. 해당 부분까지의 결과물을 확인해보면 다음과 같다.
이제 입력을 받아 계산을 하기 위해 계산 버튼을 만드는 작업을 시작해보자. 그러기 위해서는 먼저 버튼을 호출할 메서드를 추가한다.
func calculateBedtime() {
}
메서드를 추가하고 나서. toolbar를 이용하여 해당 위치에 계산 하기 버튼을 두었다.
여기서 toolbar란 apple 공식 사이트에서는 인터페이스의 가장자리를 따라 하나 이상의 버튼을 표시하는 컨트롤러라고 나와있다.
.toolbar {
Button("계산하기", action: calculateBedtime)
}
따라서 해당 위치에 계산하기 버튼을 이용하여 위에서 정의해둔 calculateBedtime 함수가 시행 되게 된다.
아직까지 함수 내부에 정의해둔 것이 없기 때문에 아직은 버튼 추가만 되었을 뿐 클릭해도 아무 일도 일어나지 않는다.
calculateBedtime함수 내부에는 이전에 미리 학습을 끝낸 Core ML 모델을 현재의 프로젝트 탐색기에 끌고 와야 한다. 이미 바탕화면이나 각자 원하는 곳에 저장해두었던 모델 파일을 끌고 온후 파일명을 원하는 대로 변경해주고 바로 시작해보자
가장 먼저 CoreML를 import 해주어야 한다. import CoreML을 해주지 않으면 이전에 만들어둔 모델들을 사용할 수없게 된다.
func calculateBedtime() {
do {
let config = MLModelConfiguration()
let model = try SleepCalculator(configuration: config)
let components = Calendar.current.dateComponents([.hour, .minute], from: wakeUp)
let hour = (components.hour ?? 0) * 60 * 60
let minute = (components.minute ?? 0) * 60
// more code here
} catch {
// something went wrong!
}
해당 모델 인스턴스는 모든 데이터를 읽고 예측을 출력하는 것이다.
예측에 실패할 확률이 낮지만 그래도 낮은 확률로 그럴 수 있기 때문에 do와 catch를 이용하여 미리 안전하게 예측을 실행하게끔 하였다.
coffeAmount의 경우 이미 커피의 양을 정수에서 Double형으로만 변환해주면 되기 때문에 큰 어려움이 없다.
하지만 wakeUp 속성의 경우 초 단위가 Double형이 아니라 date이기 때문에 사용자가 일어 나있는 시간을 파악하기 위해서는 한 단계 더 생각해야 한다. 여기서 Swift의 DateComponents 유형이 나오게 된다.
DateComponents에는 날짜를 나타내는 데 필요한 모든 부분이 개별 값으로 저장된다. 다시 말하자면 시간과 분 구성 요소를 읽고 나머지는 무시할 수 있다. 그래서 우리는 초 단위 시간을 얻으려면 60을 곱하고, 시간이 아닌 초를 얻을려면 60과 60을 곱하는 것이다.
components, hour, minute이 추가된 부분임을 확인할 수 있다.
let prediction = try model.prediction(wake: Double(hour + minute),
estimatedSleep: sleepAmount, coffee: Double(coffeeAmount))
let sleepTime = wakeUp - prediction.actualSleep //기상 시간 - 실제 수면 시간
이제 prediction을 이용하여 사용자가 실제로 얼마나 많은 수면을 필요한지를 알게 된다. 이는 앞서 학습에 이용한 Core ML 알고리즘에 의해 동적으로 계산되는 결과인 것이다.
그런데 사용자가 자야 하는 시간으로 변환하는 작업이 필요하다. 해당 작업은 사용자가 일어나야 하는 시간에서 그 값을 초 단위로 빼야 한다. 따라서 apple의 API를 이용한 아래 코드를 추가하여 새로운 시간 값을 얻게 된다.
이제는 언제 자야 하는지 정확하게 알려 줄 수 있게 되었으므로 경고 작업으로 해당 작업을 마무리해보자!
먼저 alert의 title과 경고 메시지 내부 메시지, 그리고 표시 여부를 결정하는 3가지 속성을 먼저 추가한다.
@State private var alertTitle = ""
@State private var alertMessage = ""
@State private var showingAlert = false
그리고 앞서 do-catch부분에서 해당 속성들을 이용하여 정상적으로 예측을 실패하는 경우에 대비해 오류 메시지를 출력해보자.
alertTitle = "Error"
alertMessage = "Sorry, there was a problem calculating your bedtime."
이후에 이번에는 calculateBedtime이 정상적으로 작동된 이후 예측 결과를 표현할 alert를 출력해보자.
alertTitle = "당신의 가장 이상적인 취침 시간은"
alertMessage = sleepTime.formatted(date: .omitted, time: .shortened)
경고 메시지 내부에 계산이 완료된 결괏값이 정상적으로 출력되게끔 해주었다.
이렇게 되면 해당 프로젝트의 기본적인 기능과 Core ML을 이용한 예측 과정을 한 번에 공부해볼 수 있게 된다.!
실제 나의 생활 패턴에 맞는 값을 입력해보았을 때 새벽 1시 23분에 취침하라고 정상적으로 출력된다.
이로써 이번 프로젝트도 끝!
이번 프로젝트의 경우 기계 학습에 사용한 데이터가 얼마만큼의 신뢰성을 가지고 있는지에 대한 여부는 확실하지 않다.
따라서 정확한 결과가 나오지 않더라도 예측을 통해 사용자가 보다 편안한 숙면을 취할 수 있게끔 참고용으로 사용되기 때문에 큰 문제는 없을 것 같다. 이전 예측 기능을 이용하기 위해 기계 학습에는 익숙해져 있었지만 Core ML 툴을 이용한 것은 처음이다 보니 보다 빠르고 강력하게 기계 학습을 할 수 있는 것을 보고 이후에도 기계 학습이 적용되는 앱을 제작할 때 요긴하게 사용될 것 같다.!
learning by repetition
'SwiftUI & UIKit > Tutorials' 카테고리의 다른 글
SwiftUI Tutorials - Landmarks(2) (0) | 2022.10.08 |
---|---|
SwiftUI Tutorials - Landmarks(1) (0) | 2022.10.06 |
HACKING WITH SWIFT - Guess the Flag(3) (0) | 2022.09.03 |
HACKING WITH SWIFT - Guess the Flag(2) (0) | 2022.09.03 |
HACKING WITH SWIFT - Guess the Flag (0) | 2022.08.31 |