model = torch.load(f'/content/drive/MyDrive/Colab Notebooks/SentimentAnalysisKOBert.pt')
model.eval()
!pip install flask-ngrok
!pip install flask==0.12.2
!pip install pyngrok==4.1.1
!ngrok authtoken '2E0itmXyrnKa7DoJmLdkZxE4Hk3_2hreUgB64mTNMJs6RjKfZ'
!wget https://bin.equinox.io/c/4VmDzA7iaHb/ngrok-stable-linux-amd64.tgz
!tar -xvf /content/ngrok-stable-linux-amd64.tgz
from flask import Flask, jsonify, request
from flask_ngrok import run_with_ngrok
import requests
app = Flask(__name__)
app.config['JSONIFY_PRETTYPRINT_REGULAR'] = False
run_with_ngrok(app) # Start ngrok when app is run
@app.route('/analysis', methods=['POST'])
def analysis():
content = request.get_json()
print(content)
text = content['content']
label = predict(text)
return jsonify({"label":str(label)})
if __name__ == '__main__':
app.run() # If address is in use, may need to terminate other sessions:
# Runtime > Manage Sessions > Terminate Other Sessions
import threading
threading.Thread(target=app.run, kwargs={'host':'0.0.0.0','port':80}).start()
analysis
주소를 통해 POST
메소드 리퀘스트 시 입력받은 텍스트를 파라미터로 넣고 예측 완료된 데이터의 라벨을 리턴하는 API 함수jsonify
로 리턴import Foundation
import Combine
class CustomDataService: DataService {
var sentimentAnalysisPublisher: Published<SentimentModel?>.Publisher {
$sentimentAnalysis
}
@Published var sentimentAnalysis: SentimentModel? = nil
var cancellabes = Set<AnyCancellable>()
func fetchSentimentAnalysis(_ text: String) {
guard let urlRequest = getURLRequest(text) else { return }
URLSession.shared.dataTaskPublisher(for: urlRequest)
.subscribe(on: DispatchQueue.global(qos: .background))
.receive(on: DispatchQueue.main)
.tryMap(handleOutput)
.decode(type: SentimentModel.self, decoder: JSONDecoder())
.sink { completion in
switch completion {
case .finished:
print("SUCCESS")
break
case .failure(let error):
print(error.localizedDescription)
}
} receiveValue: { [weak self] returnedData in
guard let self = self else { return }
self.sentimentAnalysis = returnedData
}
.store(in: &cancellabes)
}
private func getURL() -> URL? {
let urlString = CustomAPI.sentiment.urlString
guard let url = URL(string: urlString) else { return nil }
return url
}
private func getURLRequest(_ body: String) -> URLRequest? {
guard let url = getURL(), let body = try? JSONSerialization.data(withJSONObject: ["content" : body]) else { return nil }
var urlRequest = URLRequest(url: url)
urlRequest.httpMethod = "POST"
urlRequest.setValue(CustomAPI.sentiment.clientContent, forHTTPHeaderField: CustomAPI.sentiment.clientHeaderContentType)
urlRequest.httpBody = body
return urlRequest
}
private func handleOutput(output: URLSession.DataTaskPublisher.Output) throws -> Data {
guard
let response = output.response as? HTTPURLResponse,
response.statusCode == 200 else { throw URLError(.badServerResponse) }
return output.data
}
}
enum CustomAPI {
case sentiment
var clientHeaderContentType: String {
switch self {
case .sentiment: return "Content-Type"
}
}
var clientContent: String {
switch self {
case .sentiment: return "application/json"
}
}
var urlString: String {
switch self {
case .sentiment: [YOUR RETURNED NGROK URL]
}
}
}
urlString
을 변경해주어야 함struct SentimentModel: Codable {
let label: String?
let document: Document?
let sentences: [Sentence]?
var labelString: String {
guard let label = label else { return "" }
switch label {
case "0": return "기쁨"
case "1": return "불안"
case "2": return "당황"
case "3": return "슬픔"
case "4": return "분노"
case "5": return "상처"
default: return ""
}
}
var labelImageString: String {
guard let label = label else { return "" }
switch label {
case "0": return "funny"
case "1": return "anxious"
case "2": return "nervous"
case "3": return "sad"
case "4": return "anger"
case "5": return "hurt"
default: return ""
}
}
}
label
프로퍼티 추가 → 기존의 document
, sentences
등 클로바 API에서 필요한 데이터는 옵셔널 선언을 통한 JSON 디코딩 시 자동으로 널값으로 채우기labelString
, labelImageString
등 리턴받은 라벨 데이터를 사용하는 연산 프로퍼티 → 주어진 라벨이 어떤 감정인지, 감정 정보 및 이모티콘 정보 리턴 if let sentimentAnalysis = viewModel.sentimentAnalysis {
if let document = sentimentAnalysis.document {
Text(document.sentiment)
.font(.headline)
.fontWeight(.semibold)
.withDefaultViewModifier()
}
Image(sentimentAnalysis.labelImageString)
.resizable()
.scaledToFit()
.frame(width: 200, height: 200)
Text(sentimentAnalysis.labelString)
.font(.headline)
.fontWeight(.semibold)
}
document
를, 커스텀 모델을 사용할 때에는 label
연관 데이터를 뷰에 보여주기생각보다 KoBERT가 주어진 문장을 잘 분석하는 것 같다. 지도 학습에 있어서 커스텀 모델의 장점은, 원하는 개수/종류의 라벨을 줄 수 있어서인 것 같다. 또한 BERT 모델을 특히 한국어를 분석할 때 사용할 수 있어서 의미 깊었다.