kochat

Korean opensource chatbot framework


Keywords
chatbot, korean, kochat, deep-learning, deeplearning, korean-chatbot, sentence-classification, sequance-tagging, web-crawler
License
Apache-2.0
Install
pip install kochat==1.0.3

Documentation

Kochat

PyPI version GitHub CodeFactor

introduction_kochat



  • ์ฑ—๋ด‡ ๋นŒ๋”๋Š” ์„ฑ์— ์•ˆ์ฐจ๊ณ , ์ž์‹ ๋งŒ์˜ ๋”ฅ๋Ÿฌ๋‹ ์ฑ—๋ด‡ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋งŒ๋“œ์‹œ๊ณ  ์‹ถ์œผ์‹ ๊ฐ€์š”?
  • Kochat์„ ์ด์šฉํ•˜๋ฉด ์†์‰ฝ๊ฒŒ ์ž์‹ ๋งŒ์˜ ๋”ฅ๋Ÿฌ๋‹ ์ฑ—๋ด‡ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋นŒ๋“œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
# 1. ๋ฐ์ดํ„ฐ์…‹ ๊ฐ์ฒด ์ƒ์„ฑ
dataset = Dataset(ood=True)

# 2. ์ž„๋ฒ ๋”ฉ ํ”„๋กœ์„ธ์„œ ์ƒ์„ฑ
emb = GensimEmbedder(model=embed.FastText())

# 3. ์˜๋„(Intent) ๋ถ„๋ฅ˜๊ธฐ ์ƒ์„ฑ
clf = DistanceClassifier(
    model=intent.CNN(dataset.intent_dict),                  
    loss=CenterLoss(dataset.intent_dict)                    
)

# 4. ๊ฐœ์ฒด๋ช…(Named Entity) ์ธ์‹๊ธฐ ์ƒ์„ฑ                                                     
rcn = EntityRecognizer(
    model=entity.LSTM(dataset.entity_dict),
    loss=CRFLoss(dataset.entity_dict)
)

# 5. ๋”ฅ๋Ÿฌ๋‹ ์ฑ—๋ด‡ RESTful API ํ•™์Šต & ๋นŒ๋“œ
kochat = KochatApi(
    dataset=dataset, 
    embed_processor=(emb, True), 
    intent_classifier=(clf, True),
    entity_recognizer=(rcn, True), 
    scenarios=[
        weather, dust, travel, restaurant
    ]
)

# 6. View ์†Œ์ŠคํŒŒ์ผ๊ณผ ์—ฐ๊ฒฐ                                                                                                        
@kochat.app.route('/')
def index():
    return render_template("index.html")

# 7. ์ฑ—๋ด‡ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์„œ๋ฒ„ ๊ฐ€๋™                                                          
if __name__ == '__main__':
    kochat.app.template_folder = kochat.root_dir + 'templates'
    kochat.app.static_folder = kochat.root_dir + 'static'
    kochat.app.run(port=8080, host='0.0.0.0')



Why Kochat?

  • ํ•œ๊ตญ์–ด๋ฅผ ์ง€์›ํ•˜๋Š” ์ตœ์ดˆ์˜ ์˜คํ”ˆ์†Œ์Šค ๋”ฅ๋Ÿฌ๋‹ ์ฑ—๋ด‡ ํ”„๋ ˆ์ž„์›Œํฌ์ž…๋‹ˆ๋‹ค. (๋นŒ๋”์™€๋Š” ๋‹ค๋ฆ…๋‹ˆ๋‹ค.)
  • ๋‹ค์–‘ํ•œ Pre built-in ๋ชจ๋ธ๊ณผ Lossํ•จ์ˆ˜๋ฅผ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. NLP๋ฅผ ์ž˜ ๋ชฐ๋ผ๋„ ์ฑ—๋ด‡์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์ž์‹ ๋งŒ์˜ ์ปค์Šคํ…€ ๋ชจ๋ธ, Lossํ•จ์ˆ˜๋ฅผ ์ ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. NLP ์ „๋ฌธ๊ฐ€์—๊ฒ ๋”์šฑ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • ์ฑ—๋ด‡์— ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ ์ „์ฒ˜๋ฆฌ, ๋ชจ๋ธ, ํ•™์Šต ํŒŒ์ดํ”„๋ผ์ธ, RESTful API๊นŒ์ง€ ๋ชจ๋“  ๋ถ€๋ถ„์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
  • ๊ฐ€๊ฒฉ ๋“ฑ์„ ์‹ ๊ฒฝ์“ธ ํ•„์š” ์—†์œผ๋ฉฐ, ์•ž์œผ๋กœ๋„ ์ญ‰ ์˜คํ”ˆ์†Œ์Šค ํ”„๋กœ์ ํŠธ๋กœ ์ œ๊ณตํ•  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.
  • ์•„๋ž˜์™€ ๊ฐ™์€ ๋‹ค์–‘ํ•œ ์„ฑ๋Šฅ ํ‰๊ฐ€ ๋ฉ”ํŠธ๋ฆญ๊ณผ ๊ฐ•๋ ฅํ•œ ์‹œ๊ฐํ™” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.




Table of contents

1. Kochat ์ด๋ž€?

Kochat์€ ํ•œ๊ตญ์–ด ์ „์šฉ ์ฑ—๋ด‡ ๊ฐœ๋ฐœ ํ”„๋ ˆ์ž„์›Œํฌ๋กœ, ๋จธ์‹ ๋Ÿฌ๋‹ ๊ฐœ๋ฐœ์ž๋ผ๋ฉด ๋ˆ„๊ตฌ๋‚˜ ๋ฌด๋ฃŒ๋กœ ์†์‰ฝ๊ฒŒ ํ•œ๊ตญ์–ด ์ฑ—๋ด‡์„ ๊ฐœ๋ฐœ ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋•๋Š” ์˜คํ”ˆ์†Œ์Šค ํ”„๋ ˆ์ž„์›Œํฌ์ž…๋‹ˆ๋‹ค. ๋‹จ์ˆœ Chit-chat์ด ์•„๋‹Œ ์‚ฌ์šฉ์ž์—๊ฒŒ ์—ฌ๋Ÿฌ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋Š” ์ƒ์šฉํ™” ๋ ˆ๋ฒจ์˜ ์ฑ—๋ด‡ ๊ฐœ๋ฐœ์€ ๋‹จ์ผ ๋ชจ๋ธ๋งŒ์œผ๋กœ ๊ฐœ๋ฐœ๋˜๋Š” ๊ฒฝ์šฐ๋ณด๋‹ค ๋‹ค์–‘ํ•œ ๋ฐ์ดํ„ฐ, configuration, ML๋ชจ๋ธ, Restful Api ๋ฐ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜, ๋˜ ์ด๋“ค์„ ์œ ๊ธฐ์ ์œผ๋กœ ์—ฐ๊ฒฐํ•  ํŒŒ์ดํ”„๋ผ์ธ์„ ๊ฐ–์ถ”์–ด์•ผ ํ•˜๋Š”๋ฐ ์ด ๊ฒƒ์„ ์ฒ˜์Œ๋ถ€ํ„ฐ ๊ฐœ๋ฐœ์ž๊ฐ€ ์Šค์Šค๋กœ ๊ตฌํ˜„ํ•˜๋Š” ๊ฒƒ์€ ๊ต‰์žฅํžˆ ๋ฒˆ๊ฑฐ๋กญ๊ณ  ์†์ด ๋งŽ์ด ๊ฐ€๋Š” ์ž‘์—…์ž…๋‹ˆ๋‹ค. ์‹ค์ œ๋กœ ์ฑ—๋ด‡ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์ง์ ‘ ๊ตฌํ˜„ํ•˜๋‹ค๋ณด๋ฉด ์•„๋ž˜ ๊ทธ๋ฆผ์ฒ˜๋Ÿผ ์‹ค์งˆ์ ์œผ๋กœ ๋ชจ๋ธ ๊ฐœ๋ฐœ๋ณด๋‹ค๋Š” ์ด๋Ÿฐ ๋ถ€๋ถ„๋“ค์— ํ›จ์”ฌ ์‹œ๊ฐ„๊ณผ ๋…ธ๋ ฅ์ด ๋งŽ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

01_introduction_mlcode

Kochat์€ ์ด๋Ÿฌํ•œ ๋ถ€๋ถ„์„ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ์ œ์ž‘๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๋ฐ์ดํ„ฐ ์ „์ฒ˜๋ฆฌ, ์•„ํ‚คํ…์ฒ˜, ๋ชจ๋ธ๊ณผ์˜ ํŒŒ์ดํ”„๋ผ์ธ, ์‹คํ—˜ ๊ฒฐ๊ณผ ์‹œ๊ฐํ™”, ์„ฑ๋Šฅํ‰๊ฐ€ ๋“ฑ์€ Kochat์˜ ๊ตฌ์„ฑ์„ ์‚ฌ์šฉํ•˜๋ฉด์„œ ๊ฐœ๋ฐœ์ž๊ฐ€ ์›ํ•˜๋Š” ๋ชจ๋ธ์ด๋‚˜ Lossํ•จ์ˆ˜, ๋ฐ์ดํ„ฐ ์…‹ ๋“ฑ๋งŒ ๊ฐ„๋‹จํ•˜๊ฒŒ ์ž‘์„ฑํ•˜์—ฌ ๋‚ด๊ฐ€ ์›ํ•˜๋Š” ๋ชจ๋ธ์˜ ์„ฑ๋Šฅ์„ ๋น ๋ฅด๊ฒŒ ์‹คํ—˜ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋„์™€์ค๋‹ˆ๋‹ค. ๋˜ํ•œ ํ”„๋ฆฌ ๋นŒํŠธ์ธ ๋ชจ๋ธ๋“ค๊ณผ Loss ํ•จ์ˆ˜๋“ฑ์„ ์ง€์›ํ•˜์—ฌ ๋”ฅ๋Ÿฌ๋‹์ด๋‚˜ ์ž์—ฐ์–ด์ฒ˜๋ฆฌ์— ๋Œ€ํ•ด ์ž˜ ๋ชจ๋ฅด๋”๋ผ๋„ ํ”„๋กœ์ ํŠธ์— ๋ฐ์ดํ„ฐ๋งŒ ์ถ”๊ฐ€ํ•˜๋ฉด ์†์‰ฝ๊ฒŒ ์ƒ๋‹นํžˆ ๋†’์€ ์„ฑ๋Šฅ์˜ ์ฑ—๋ด‡์„ ๊ฐœ๋ฐœํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋„์™€์ค๋‹ˆ๋‹ค. ์•„์ง์€ ์ดˆ๊ธฐ๋ ˆ๋ฒจ์ด๊ธฐ ๋•Œ๋ฌธ์— ๋งŽ์€ ๋ชจ๋ธ๊ณผ ๊ธฐ๋Šฅ์„ ์ง€์›ํ•˜์ง€๋Š” ์•Š์ง€๋งŒ ์ ์ฐจ ๋ชจ๋ธ๊ณผ ๊ธฐ๋Šฅ์„ ๋Š˜๋ ค๋‚˜๊ฐˆ ๊ณ„ํš์ž…๋‹ˆ๋‹ค.

1.1. ๊ธฐ์กด ์ฑ—๋ด‡ ๋นŒ๋”์™€์˜ ์ฐจ์ด์ 

  • ๊ธฐ์กด์— ์ƒ์šฉํ™”๋œ ๋งŽ์€ ์ฑ—๋ด‡ ๋นŒ๋”์™€ Kochat์€ ํƒ€๊นƒ์œผ๋กœ ํ•˜๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ๋‹ค๋ฆ…๋‹ˆ๋‹ค. ์ƒ์šฉํ™”๋œ ์ฑ—๋ด‡ ๋นŒ๋”๋“ค์€ ๋งค์šฐ ๊ฐ„ํŽธํ•œ ์›น ๊ธฐ๋ฐ˜์˜ UX/UI๋ฅผ ์ œ๊ณตํ•˜๋ฉฐ ์ผ๋ฐ˜์ธ์„ ํƒ€๊นƒ์œผ๋กœ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ์— ๋ฐ˜ํ•ด Kochat์€ ์ฑ—๋ด‡๋นŒ๋” ๋ณด๋‹ค๋Š” ๊ฐœ๋ฐœ์ž๋ฅผ ํƒ€๊นƒ์œผ๋กœํ•˜๋Š” ํ”„๋ ˆ์ž„์›Œํฌ์— ๊ฐ€๊น์Šต๋‹ˆ๋‹ค. ๊ฐœ๋ฐœ์ž๋Š” ์†Œ์Šค์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•จ์— ๋”ฐ๋ผ์„œ ํ”„๋ ˆ์ž„์›Œํฌ์— ๋ณธ์ธ๋งŒ์˜ ๋ชจ๋ธ์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๊ณ , Loss ํ•จ์ˆ˜๋ฅผ ๋ฐ”๊พธ๊ฑฐ๋‚˜ ๋ณธ์ธ์ด ์›ํ•˜๋ฉด ์•„์˜ˆ ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ์„ ์ฒจ๊ฐ€ํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

  • Kochat์€ ์˜คํ”ˆ์†Œ์Šค ํ”„๋กœ์ ํŠธ์ž…๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ๋งŽ์€ ์‚ฌ๋žŒ์ด ์ฐธ์—ฌํ•ด์„œ ํ•จ๊ป˜ ๊ฐœ๋ฐœํ•  ์ˆ˜ ์žˆ๊ณ  ๋งŒ์•ฝ ์ƒˆ๋กœ์šด ๋ชจ๋ธ์„ ๊ฐœ๋ฐœํ•˜๊ฑฐ๋‚˜ ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•˜๊ณ ์‹ถ๋‹ค๋ฉด ์–ผ๋งˆ๋“ ์ง€ ๋ ˆํฌ์ง€ํ† ๋ฆฌ์— ์ปจํŠธ๋ฆฌ๋ทฐ์…˜ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • Kochat์€ ๋ฌด๋ฃŒ์ž…๋‹ˆ๋‹ค. ๋งค๋‹ฌ ์‚ฌ์šฉ๋ฃŒ๋ฅผ ๋‚ด์•ผํ•˜๋Š” ์ฑ—๋ด‡ ๋นŒ๋”๋“ค์— ๋น„ํ•ด ์ž์ฒด์ ์ธ ์„œ๋ฒ„๋งŒ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค๋ฉด ๋น„์šฉ์ œ์•ฝ ์—†์ด ์–ผ๋งˆ๋“ ์ง€ ์ฑ—๋ด‡์„ ๊ฐœ๋ฐœํ•˜๊ณ  ์„œ๋น„์Šค ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์•„์ง์€ ๊ธฐ๋Šฅ์ด ๋ฏธ์•ฝํ•˜์ง€๋งŒ ์ถ”ํ›„์—๋Š” ์ •๋ง ์›ฌ๋งŒํ•œ ์ฑ—๋ด‡ ๋นŒ๋”๋“ค ๋ณด๋‹ค ๋” ๋‹ค์–‘ํ•œ ๊ธฐ๋Šฅ์„ ๋ฌด๋ฃŒ๋กœ ์ œ๊ณตํ•  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.

1.2. Kochat ์ œ์ž‘ ๋™๊ธฐ

01_introduction_rasa

์ด์ „์— ์—ฌ๊ธฐ์ €๊ธฐ์„œ ์ฝ”๋“œ๋ฅผ ๊ธ์–ด๋ชจ์•„์„œ ๋งŒ๋“ , ์ˆ˜์ค€ ๋‚ฎ์€ ์ œ ๋”ฅ๋Ÿฌ๋‹ chatbot ๋ ˆํฌ์ง€ํ† ๋ฆฌ๊ฐ€ ์ƒ๊ฐ๋ณด๋‹ค ํฐ ๊ด€์‹ฌ์„ ๋ฐ›์œผ๋ฉด์„œ, ํ•œ๊ตญ์–ด๋กœ ๋œ ๋”ฅ๋Ÿฌ๋‹ ์ฑ—๋ด‡ ๊ตฌํ˜„์ฒด๊ฐ€ ์ •๋ง ๋งŽ์ด ์—†๋‹ค๋Š” ๊ฒƒ์„ ๋Š๊ผˆ์Šต๋‹ˆ๋‹ค. ํ˜„์žฌ ๋Œ€๋ถ€๋ถ„์˜ ์ฑ—๋ด‡ ๋นŒ๋”๋“ค์€ ๋Œ€๋ถ€๋ถ„ ์ผ๋ฐ˜์ธ์„ ๊ฒจ๋ƒฅํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์›น์ƒ์—์„œ ์†์‰ฌ์šด UX/UI ๊ธฐ๋ฐ˜์œผ๋กœ ์„œ๋น„์Šค ์ค‘์ž…๋‹ˆ๋‹ค. ์ผ๋ฐ˜์ธ ์‚ฌ์šฉ์ž๋Š” ์‚ฌ์šฉํ•˜๊ธฐ ํŽธ๋ฆฌํ•˜๊ฒ ์ง€๋งŒ, ์ €์™€ ๊ฐ™์€ ๊ฐœ๋ฐœ์ž๋“ค์€ ๋ชจ๋ธ๋„ ์ปค์Šคํ„ฐ๋งˆ์ด์ง• ํ•˜๊ณ  ์‹ถ๊ณ , ๋กœ์Šคํ•จ์ˆ˜๋„ ๋ฐ”๊ฟ”๋ณด๊ณ ์‹ถ๊ณ , ์‹œ๊ฐํ™”๋„ ํ•˜๋ฉด์„œ ๋”์šฑ ๋†’์€ ์„ฑ๋Šฅ์„ ์ถ”๊ตฌํ•˜๊ณ  ์‹ถ์ง€๋งŒ ์•„์‰ฝ๊ฒŒ๋„ ํ•œ๊ตญ์–ด ์ฑ—๋ด‡ ๋นŒ๋” ์ค‘์—์„œ ์ด๋Ÿฌํ•œ ๋ฐฉ์‹์œผ๋กœ ์ž˜ ์•Œ๋ ค์ง„ ๊ฒƒ์€ ์—†์Šต๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋˜ ์ค‘, ๋ฏธ๊ตญ์˜ RASA๋ผ๋Š” ์ฑ—๋ด‡ ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ๋ณด๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. RASA๋Š” ๊ฐœ๋ฐœ์ž๊ฐ€ ์ง์ ‘ ์†Œ์Šค์ฝ”๋“œ๋ฅผ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋‹ค์–‘ํ•œ ๋ถ€๋ถ„์„ ์ปค์Šคํ„ฐ๋งˆ์ด์ง• ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ํ•œ๊ตญ์–ด๋ฅผ ์ œ๋Œ€๋กœ ์ง€์›ํ•˜์ง€ ์•Š์•„์„œ, ์ „์šฉ ํ† ํฌ๋‚˜์ด์ €๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๋“ฑ ๋งค์šฐ ๋ฒˆ๊ฑฐ๋กœ์šด ์ž‘์—…์ด ํ•„์š”ํ•˜๊ณ  ์‹ค์ œ๋กœ ๋„ˆ๋ฌด ๋‹ค์–‘ํ•œ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์กด์žฌํ•˜์—ฌ ์ต์ˆ™ํ•ด์ง€๋Š”๋ฐ ์กฐ๊ธˆ ์–ด๋ ค์šด ํŽธ์ž…๋‹ˆ๋‹ค. ๋•Œ๋ฌธ์— ๋ˆ„๊ตฐ๊ฐ€ ํ•œ๊ตญ์–ด ๊ธฐ๋ฐ˜์ด๋ฉด์„œ ์กฐ๊ธˆ ๋” ์ปดํŒฉํŠธํ•œ ์ฑ—๋ด‡ ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ์ œ์ž‘ํ•œ๋‹ค๋ฉด ์ฑ—๋ด‡์„ ๊ฐœ๋ฐœํ•ด์•ผํ•˜๋Š” ๊ฐœ๋ฐœ์ž๋“ค์—๊ฒŒ ์ •๋ง ์œ ์šฉํ•  ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐ๋˜์—ˆ๊ณ  ์ง์ ‘ ์ด๋Ÿฌํ•œ ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ๋งŒ๋“ค์–ด๋ณด์ž๋Š” ์ƒ๊ฐ์— Kochat์„ ์ œ์ž‘ํ•˜๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

Kochat์€ ํ•œ๊ตญ์–ด(Korean)์˜ ์•ž๊ธ€์ž์ธ Ko์™€ ์ œ ์ด๋ฆ„ ์•ž ๊ธ€์ž์ธ Ko๋ฅผ ๋”ฐ์™€์„œ ์ง€์—ˆ์Šต๋‹ˆ๋‹ค. Kochat์€ ์•ž์œผ๋กœ๋„ ๊ณ„์† ์˜คํ”ˆ์†Œ์Šค ํ”„๋กœ์ ํŠธ๋กœ ์œ ์ง€๋  ๊ฒƒ์ด๋ฉฐ, ์ ์–ด๋„ 1~2๋‹ฌ์— 1๋ฒˆ ์ด์ƒ์€ ์ƒˆ๋กœ์šด ๋ชจ๋ธ์„ ์ถ”๊ฐ€ํ•˜๊ณ , ๊ธฐ์กด ์†Œ์Šค์ฝ”๋“œ์˜ ๋ฒ„๊ทธ๋ฅผ ์ˆ˜์ •ํ•˜๋Š” ๋“ฑ ์œ ์ง€๋ณด์ˆ˜ ์ž‘์—…์„ ์ด์–ด๊ฐˆ ๊ฒƒ์ด๋ฉฐ ์ฒ˜์Œ์—๋Š” ๋ฏธ์ฒœํ•œ ์‹ค๋ ฅ์ธ ์ œ๊ฐ€ ์‹œ์ž‘ํ–ˆ์ง€๋งŒ, ๊ทธ ๋์€ RASA์ฒ˜๋Ÿผ ์ •๋ง ์œ ์šฉํ•˜๊ณ  ๋†’์€ ์„ฑ๋Šฅ์„ ๋ณด์—ฌ์ฃผ๋Š” ์ˆ˜์ค€๋†’์€ ์˜คํ”ˆ์†Œ์Šค ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ€ ๋˜์—ˆ์œผ๋ฉด ์ข‹๊ฒ ์Šต๋‹ˆ๋‹ค. :)




2. About Chatbot

์ด ์ฑ•ํ„ฐ์—์„œ๋Š” ์ฑ—๋ด‡์˜ ๋ถ„๋ฅ˜์™€ ๊ตฌํ˜„๋ฐฉ๋ฒ•, Kochat์€ ์–ด๋–ป๊ฒŒ ์ฑ—๋ด‡์„ ๊ตฌํ˜„ํ•˜๊ณ  ์žˆ๋Š”์ง€์— ๋Œ€ํ•ด ๊ฐ„๋‹จํ•˜๊ฒŒ ์†Œ๊ฐœํ•ฉ๋‹ˆ๋‹ค.

2.1. ์ฑ—๋ด‡์˜ ๋ถ„๋ฅ˜

chatbot_table

์ฑ—๋ด‡์€ ํฌ๊ฒŒ ๋น„๋ชฉ์ ๋Œ€ํ™”๋ฅผ ์œ„ํ•œ Open domain ์ฑ—๋ด‡๊ณผ ๋ชฉ์ ๋Œ€ํ™”๋ฅผ ์œ„ํ•œ Close domain ์ฑ—๋ด‡์œผ๋กœ ๋‚˜๋‰ฉ๋‹ˆ๋‹ค. Open domain ์ฑ—๋ด‡์€ ์ฃผ๋กœ ์žก๋‹ด ๋“ฑ์„ ์ˆ˜ํ–‰ํ•˜๋Š” ์ฑ—๋ด‡์„ ์˜๋ฏธํ•˜๋Š”๋ฐ, ์—ฌ๋Ÿฌ๋ถ„์ด ์ž˜ ์•Œ๊ณ ์žˆ๋Š” ์‹ฌ์‹ฌ์ด ๋“ฑ์ด ์ฑ—๋ด‡์ด ๋Œ€ํ‘œ์ ์ธ Open domain ์ฑ—๋ด‡์ด๋ฉฐ Chit-chat์ด๋ผ๊ณ ๋„ ๋ถˆ๋ฆฝ๋‹ˆ๋‹ค. Close domain ์ฑ—๋ด‡์ด๋ž€ ํ•œ์ •๋œ ๋Œ€ํ™” ๋ฒ”์œ„ ์•ˆ์—์„œ ์‚ฌ์šฉ์ž๊ฐ€ ์›ํ•˜๋Š” ๋ชฉ์ ์„ ๋‹ฌ์„ฑํ•˜๊ธฐ ์œ„ํ•œ ์ฑ—๋ด‡์œผ๋กœ ์ฃผ๋กœ ๊ธˆ์œต์ƒ๋‹ด๋ด‡, ์‹๋‹น์˜ˆ์•ฝ๋ด‡ ๋“ฑ์ด ์ด์— ํ•ด๋‹นํ•˜๋ฉฐ Goal oriented ์ฑ—๋ด‡์ด๋ผ๊ณ ๋„ ๋ถˆ๋ฆฝ๋‹ˆ๋‹ค. ์š”์ฆ˜ ์ถœ์‹œ๋˜๋Š” ์‹œ๋ฆฌ๋‚˜ ๋น…์Šค๋น„ ๊ฐ™์€ ์ธ๊ณต์ง€๋Šฅ ๋น„์„œ, ์ธ๊ณต์ง€๋Šฅ ์Šคํ”ผ์ปค๋“ค์€ ํŠน์ˆ˜ ๊ธฐ๋Šฅ๋„ ์ˆ˜ํ–‰ํ•ด์•ผํ•˜๊ณ  ์‚ฌ์šฉ์ž์™€ ์žก๋‹ด๋„ ์ž˜ ํ•ด์•ผํ•˜๋ฏ€๋กœ Open domain ์ฑ—๋ด‡๊ณผ Close domain ์ฑ—๋ด‡์ด ๋ชจ๋‘ ํฌํ•จ๋˜์–ด ์žˆ๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์Šต๋‹ˆ๋‹ค.

2.2. ์ฑ—๋ด‡์˜ ๊ตฌํ˜„

์ฑ—๋ด‡์„ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ํฌ๊ฒŒ ํ†ต๊ณ„๊ธฐ๋ฐ˜์˜ ์ฑ—๋ด‡๊ณผ ๋”ฅ๋Ÿฌ๋‹ ๊ธฐ๋ฐ˜์˜ ์ฑ—๋ด‡์œผ๋กœ ๋‚˜๋‰ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์—์„œ๋Š” ๋”ฅ๋Ÿฌ๋‹ ๊ธฐ๋ฐ˜์˜ ์ฑ—๋ด‡๋งŒ ์†Œ๊ฐœํ•˜๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

2.2.1. Open domain ์ฑ—๋ด‡

chatbot_seq2seq

๋จผ์ € Open domain ์ฑ—๋ด‡์˜ ๊ฒฝ์šฐ๋Š” ๋”ฅ๋Ÿฌ๋‹ ๋ถ„์•ผ์—์„œ๋Š” ๋Œ€๋ถ€๋ถ„, End to End ์‹ ๊ฒฝ๋ง ๊ธฐ๊ณ„๋ฒˆ์—ญ ๋ฐฉ์‹(Seq2Seq)์œผ๋กœ ๊ตฌํ˜„๋˜์–ด์™”์Šต๋‹ˆ๋‹ค. Seq2Seq์€ ํ•œ ๋ฌธ์žฅ์„ ๋‹ค๋ฅธ ๋ฌธ์žฅ์œผ๋กœ ๋ณ€ํ™˜/๋ฒˆ์—ญํ•˜๋Š” ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค. ๋ฒˆ์—ญ๊ธฐ์—๊ฒŒ "๋‚˜๋Š” ๋ฐฐ๊ณ ํ”„๋‹ค"๋ผ๋Š” ์ž…๋ ฅ์ด ์ฃผ์–ด์ง€๋ฉด "I'm Hungry"๋ผ๊ณ  ๋ฒˆ์—ญํ•ด๋‚ด๋“ฏ์ด, ์ฑ—๋ด‡ Seq2Seq๋Š” "๋‚˜๋Š” ๋ฐฐ๊ณ ํ”„๋‹ค"๋ผ๋Š” ์ž…๋ ฅ์ด ์ฃผ์–ด์งˆ ๋•Œ, "๋งŽ์ด ๋ฐฐ๊ณ ํ”„์‹ ๊ฐ€์š”?" ๋“ฑ์˜ ๋Œ€๋‹ต์œผ๋กœ ๋ฒˆ์—ญํ•ฉ๋‹ˆ๋‹ค. ์ตœ๊ทผ์— ๋ฐœํ‘œ๋œ Google์˜ Meena ๊ฐ™์€ ๋ชจ๋ธ์„ ๋ณด๋ฉด, ๋ณต์žกํ•œ ๋ชจ๋ธ ์•„ํ‚คํ…์ฒ˜๋‚˜ ํ•™์Šต ํ”„๋ ˆ์ž„์›Œํฌ ์—†์ด End to End (Seq2Seq) ๋ชจ๋ธ๋งŒ์œผ๋กœ๋„ ๋งค์šฐ ๋ฐฉ๋Œ€ํ•œ ๋ฐ์ดํ„ฐ์…‹๊ณผ ๋†’์€ ์„ฑ๋Šฅ์˜ ์ปดํ“จํŒ… ๋ฆฌ์†Œ์Šค๋ฅผ ํ™œ์šฉํ•˜๋ฉด ์ •๋ง ์‚ฌ๋žŒ๊ณผ ๊ทผ์ ‘ํ•œ ์ˆ˜์ค€์œผ๋กœ ๋Œ€ํ™”ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์œผ๋กœ ์•Œ๋ ค์ ธ์žˆ์Šต๋‹ˆ๋‹ค. (๊ทธ๋Ÿฌ๋‚˜ ํ˜„์žฌ๋ฒ„์ „ ํ”„๋ ˆ์ž„์›Œํฌ์—์„œ๋Š” Close domain ๋งŒ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ์ฐจํ›„ ๋ฒ„์ „์—์„œ ๋‹ค์–‘ํ•œ Seq2Seq ๋ชจ๋ธ๋„ ์ถ”๊ฐ€ํ•  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.)

2.2.2. Close domain ์ฑ—๋ด‡

chatbot_slot_filling

Close domain ์ฑ—๋ด‡์€ ๋Œ€๋ถ€๋ถ„ Slot Filling ๋ฐฉ์‹์œผ๋กœ ๊ตฌํ˜„๋˜์–ด ์™”์Šต๋‹ˆ๋‹ค. ๋ฌผ๋ก  Close domain ์ฑ—๋ด‡๋„ Open domain์ฒ˜๋Ÿผ End to end๋กœ ๊ตฌํ˜„ํ•˜๋ ค๋Š” ๋‹ค์–‘ํ•œ ์‹œ๋„ ๋“ค๋„ ์กด์žฌ ํ•˜์˜€์œผ๋‚˜, ๋…ผ๋ฌธ์—์„œ ์ œ์‹œํ•˜๋Š” ๋ฐ์ดํ„ฐ์…‹์—์„œ๋งŒ ์ž˜ ์ž‘๋™ํ•˜๊ณ , ์‹ค์ œ ๋‹ค๋ฅธ ๋ฐ์ดํ„ฐ ์…‹(Task6์˜ DSTC dataset)์— ์ ์šฉํ•˜๋ฉด ๊ทธ ์ •๋„์˜ ์„ฑ๋Šฅ์ด ๋‚˜์˜ค์ง€ ์•Š์•˜๊ธฐ ๋•Œ๋ฌธ์— ํ˜„์—…์— ์ ์šฉ๋˜๊ธฐ๋Š” ์–ด๋ ค์›€์ด ์žˆ์Šต๋‹ˆ๋‹ค. ๋•Œ๋ฌธ์— ํ˜„์žฌ๋Š” ๋Œ€๋ถ€๋ถ„์˜ ๋ชฉ์ ์ง€ํ–ฅ ์ฑ—๋ด‡ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ๊ธฐ์กด ๋ฐฉ์‹์ธ Slot Filling ๋ฐฉ์‹์œผ๋กœ ๊ตฌํ˜„๋˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

Slot Filling ๋ฐฉ์‹์€ ๋ฏธ๋ฆฌ ๊ธฐ๋Šฅ์„ ์ˆ˜ํ–‰ํ•  ์ •๋ณด๋ฅผ ๋‹ด๋Š” '์Šฌ๋กฏ'์„ ๋จผ์ € ์ •์˜ํ•œ ๋‹ค์Œ, ์‚ฌ์šฉ์ž์˜ ๋ง์„ ๋“ฃ๊ณ  ์–ด๋–ค ์Šฌ๋กฏ์„ ์„ ํƒํ• ์ง€ ์ •ํ•˜๊ณ , ํ•ด๋‹น ์Šฌ๋กฏ์„ ์ฑ„์›Œ๋‚˜๊ฐ€๋Š” ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ด๋Ÿฌํ•œ Slot Filling ๋ฐฉ์‹ ์ฑ—๋ด‡์˜ ๊ตฌํ˜„์„ ์œ„ํ•ด '์ธํ…ํŠธ'์™€ '์—”ํ‹ฐํ‹ฐ'๋ผ๋Š” ๊ฐœ๋…์ด ๋“ฑ์žฅํ•ฉ๋‹ˆ๋‹ค. ๋ง๋กœ๋งŒ ์„ค๋ช…ํ•˜๋ฉด ์–ด๋ ค์šฐ๋‹ˆ ์˜ˆ์‹œ๋ฅผ ๋ด…์‹œ๋‹ค. ๊ฐ€์žฅ ๋จผ์ € ์šฐ๋ฆฌ๊ฐ€ ์—ฌํ–‰ ์ •๋ณด ์•Œ๋ฆผ ์ฑ—๋ด‡์„ ๋งŒ๋“ ๋‹ค๊ณ  ๊ฐ€์ •ํ•˜๊ณ , ์—ฌํ–‰์ •๋ณด ์ œ๊ณต์„ ์œ„ํ•ด "๋‚ ์”จ ์ •๋ณด์ œ๊ณต", "๋ฏธ์„ธ๋จผ์ง€ ์ •๋ณด์ œ๊ณต", "๋ง›์ง‘ ์ •๋ณด์ œ๊ณต", "์—ฌํ–‰์ง€ ์ •๋ณด์ œ๊ณต"์ด๋ผ๋Š” 4๊ฐ€์ง€ ํ•ต์‹ฌ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•ด์•ผํ•œ๋‹ค๊ณ  ํ•ฉ์‹œ๋‹ค.

2.2.2.1. ์ธํ…ํŠธ(์˜๋„) ๋ถ„๋ฅ˜ํ•˜๊ธฐ : ์Šฌ๋กฏ ๊ณ ๋ฅด๊ธฐ

chatbot_intent_classification

๊ฐ€์žฅ ๋จผ์ € ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ฌธ์žฅ์„ ์ž…๋ ฅ๋ฐ›์•˜์„ ๋•Œ, ์šฐ๋ฆฌ๋Š” ์ € 4๊ฐ€์ง€ ์ •๋ณด์ œ๊ณต ๊ธฐ๋Šฅ ์ค‘ ์–ด๋–ค ๊ธฐ๋Šฅ์„ ์‹คํ–‰ํ•ด์•ผํ•˜๋Š”์ง€ ์•Œ์•„์ฑ„์•ผํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ฒƒ์„ ์ธํ…ํŠธ(Intent)๋ถ„๋ฅ˜. ์ฆ‰, ์˜๋„ ๋ถ„๋ฅ˜๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž๋กœ๋ถ€ํ„ฐ "์ˆ˜์š”์ผ ๋ถ€์‚ฐ ๋‚ ์”จ ์–ด๋– ๋‹ˆ?"๋ผ๋Š” ๋ฌธ์žฅ์ด ์ž…๋ ฅ๋˜๋ฉด 4๊ฐ€์ง€ ๊ธฐ๋Šฅ ์ค‘ ๋‚ ์”จ ์ •๋ณด์ œ๊ณต ๊ธฐ๋Šฅ์„ ์ˆ˜ํ–‰ํ•ด์•ผ ํ•œ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ์•„๋‚ด์•ผํ•ฉ๋‹ˆ๋‹ค. ๋•Œ๋ฌธ์— ๋ฌธ์žฅ ๋ฒกํ„ฐ๊ฐ€ ์ž…๋ ฅ๋˜๋ฉด, Text Classification์„ ์ˆ˜ํ–‰ํ•˜์—ฌ ์–ด๋–ค API๋ฅผ ์‚ฌ์šฉํ•ด์•ผํ• ์ง€ ์•Œ์•„๋ƒ…๋‹ˆ๋‹ค.

2.2.2.2. ํด๋ฐฑ ๊ฒ€์ถœํ•˜๊ธฐ : ๋ชจ๋ฅด๊ฒ ์œผ๋ฉด ๋ชจ๋ฅธ๋‹ค๊ณ  ๋งํ•˜๊ธฐ

๊ทธ๋Ÿฌ๋‚˜ ์—ฌ๊ธฐ์— ์‹ ๊ฒฝ์จ์•ผํ•  ๋ถ€๋ถ„์ด ํ•œ ๋ถ€๋ถ„ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค. ์ผ๋ฐ˜์ ์ธ ๋”ฅ๋Ÿฌ๋‹ ๋ถ„๋ฅ˜๋ชจ๋ธ์€ ๋ชจ๋ธ์ด ํ•™์Šตํ•œ ํด๋ž˜์Šค ๋‚ด์—์„œ๋งŒ ๋ถ„๋ฅ˜๊ฐ€ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์‚ฌ์šฉ์ž๊ฐ€ 4๊ฐ€์ง€์˜ ๋ฐœํ™”์˜๋„ ์•ˆ์—์„œ๋งŒ ๋งํ•  ๊ฒƒ์ด๋ผ๋Š” ๋ณด์žฅ์€ ์—†์Šต๋‹ˆ๋‹ค. ๋งŒ์•ฝ ์œ„์ฒ˜๋Ÿผ "๋‚ ์”จ ์ •๋ณด์ œ๊ณต", "๋ฏธ์„ธ๋จผ์ง€ ์ •๋ณด์ œ๊ณต", "๋ง›์ง‘ ์ •๋ณด์ œ๊ณต", "์—ฌํ–‰์ง€ ์ •๋ณด์ œ๊ณต"์˜ ๋ฐ์ดํ„ฐ๋งŒ ํ•™์Šตํ•œ ์ธํ…ํŠธ ๋ถ„๋ฅ˜๋ชจ๋ธ์— "์•ˆ๋…• ๋ฐ˜๊ฐ‘๋‹ค."๋ผ๋Š” ๋ง์„ ํ•˜๊ฒŒ ๋˜๋ฉด ์–ด๋–ป๊ฒŒ ๋ ๊นŒ์š”? ์œ„ 4๊ฐ€์ง€์— ์†ํ•˜์ง€ ์•Š์€ ๋ฐœํ™” ์˜๋„์ธ "์ธ์‚ฌ"์— ํ•ด๋‹นํ•˜์ง€๋งŒ ๋ชจ๋ธ์€ ์ธ์‚ฟ๋ง์€ ํ•œ๋ฒˆ๋„ ๋ณธ์ ์ด ์—†๊ธฐ ๋•Œ๋ฌธ์— ์ด๊ฒƒ๋„ ์—ญ์‹œ 4๊ฐ€์ง€ ์ค‘ ํ•˜๋‚˜๋กœ ๋ถ„๋ฅ˜ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ์˜๋„ ๋ถ„๋ฅ˜๋ชจ๋ธ์—๋Š” ๋ฐ˜๋“œ์‹œ ํด๋ฐฑ (Fallback) ๊ฒ€์ถœ ์ „๋žต์ด ํฌํ•จ๋˜์–ด์•ผํ•ฉ๋‹ˆ๋‹ค.

fallback

๋ณดํ†ต์˜ ์ฑ—๋ด‡๋นŒ๋”๋“ค์€ ์ž…๋ ฅ ๋‹จ์–ด๋“ค์˜ ์ž„๋ฒ ๋”ฉ์ธ ๋ฌธ์žฅ ๋ฒกํ„ฐ์™€ ๊ธฐ์กด ๋ฐ์ดํ„ฐ์…‹์— ์žˆ๋Š” ๋ฌธ์žฅ ๋ฒกํ„ฐ๋“ค์˜ Cosine ์œ ์‚ฌ๋„๋ฅผ ๋น„๊ตํ•ฉ๋‹ˆ๋‹ค. ์ด ๋•Œ ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด ์ธ์ ‘ ํด๋ž˜์Šค์™€์˜ ๊ฐ๋„๊ฐ€ ์ž„๊ณ„์น˜ ์ด์ƒ์ด๋ฉด Fallback์ด๊ณ , ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด ์ธ์ ‘ ํด๋ž˜์Šค๋กœ ๋ฐ์ดํ„ฐ ์ƒ˜ํ”Œ์„ ๋ถ„๋ฅ˜ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์•„๋ž˜ ๊ทธ๋ฆผ์„ ๋ณด๋ฉด ์ผ๋ฐ˜์ ์ธ ์ฑ—๋ด‡ ๋นŒ๋”๋“ค์ด ์–ด๋–ค์‹์œผ๋กœ Fallback์„ ๊ฒ€์ถœํ•˜๋Š”์ง€ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

fallback

Kochat์€ ์ด๋ ‡๊ฒŒ ๋‹จ์ˆœํžˆ ๋ฌธ์žฅ๋“ค์˜ ๋ฒกํ„ฐ Cosine ์œ ์‚ฌ๋„๋ฅผ ๋น„๊ตํ•˜์ง€ ์•Š๊ณ  ๋”์šฑ ๊ณ ์ฐจ์›์ ์ธ ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•˜์—ฌ Fallback ๋””ํ…์…˜์„ ๋ณด๋‹ค ๋” ์ž˜ ์ˆ˜ํ–‰ํ•˜๋„๋ก ์„ค๊ณ„ํ•˜์˜€๋Š”๋ฐ ์ด์— ๋Œ€ํ•œ ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์•„๋ž˜์˜ Usage์—์„œ ์ž์„ธํžˆ ์–ธ๊ธ‰ํ•˜๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

2.2.2.3. ์—”ํ‹ฐํ‹ฐ(๊ฐœ์ฒด๋ช…) ์ธ์‹ํ•˜๊ธฐ : ์Šฌ๋กฏ ์ฑ„์šฐ๊ธฐ

chatbot_entity_recognition

๊ทธ ๋‹ค์Œ ํ•ด์•ผํ•  ์ผ์€ ๋ฐ”๋กœ ๊ฐœ์ฒด๋ช…์ธ์‹ (Named Entity Recognition)์ž…๋‹ˆ๋‹ค. ์–ด๋–ค API๋ฅผ ํ˜ธ์ถœํ• ์ง€ ์•Œ์•„๋ƒˆ๋‹ค๋ฉด, ์ด์ œ ๊ทธ API๋ฅผ ํ˜ธ์ถœํ•˜๊ธฐ ์œ„ํ•œ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์ฐพ์•„์•ผํ•ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ ๋‚ ์”จ API์˜ ์‹คํ–‰์„ ์œ„ํ•œ ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ "์ง€์—ญ"๊ณผ "๋‚ ์”จ"๋ผ๋ฉด ์‚ฌ์šฉ์ž์˜ ์ž…๋ ฅ ๋ฌธ์žฅ์—์„œ "์ง€์—ญ"์— ๊ด€๋ จ๋œ ์ •๋ณด์™€ "๋‚ ์”จ"์— ๊ด€๋ จ๋œ ์ •๋ณด๋ฅผ ์ฐพ์•„๋‚ด์„œ ํ•ด๋‹น ์Šฌ๋กฏ์„ ์ฑ„์›๋‹ˆ๋‹ค. ๋งŒ์•ฝ ์‚ฌ์šฉ์ž๊ฐ€ "์ˆ˜์š”์ผ ๋‚ ์”จ ์•Œ๋ ค์ค˜"๋ผ๊ณ ๋งŒ ๋งํ–ˆ๋‹ค๋ฉด, ์ง€์—ญ์— ๊ด€๋ จ๋œ ์ •๋ณด๋Š” ์•„์ง ์ฐพ์•„๋‚ด์ง€ ๋ชปํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ๋‹ค์‹œ ๋˜๋ฌผ์–ด์„œ ์ฐพ์•„๋‚ด์•ผํ•ฉ๋‹ˆ๋‹ค.

2.2.2.4. API ํ˜ธ์ถœํ•˜๊ธฐ : ๋Œ€๋‹ต ์ƒ์„ฑํ•˜๊ธฐ

chatbot_response_generation

์Šฌ๋กฏ์ด ๋ชจ๋‘ ์ฑ„์›Œ์กŒ๋‹ค๋ฉด API๋ฅผ ์‹คํ–‰์‹œ์ผœ์„œ ์™ธ๋ถ€๋กœ๋ถ€ํ„ฐ ์ •๋ณด๋ฅผ ์ œ๊ณต๋ฐ›์Šต๋‹ˆ๋‹ค. API๋กœ๋ถ€ํ„ฐ ๊ฒฐ๊ณผ๊ฐ€ ๋„์ฐฉํ•˜๋ฉด, ๋ฏธ๋ฆฌ ๋งŒ๋“ค์–ด๋‘” ํ…œํ”Œ๋ฆฟ ๋ฌธ์žฅ์— ํ•ด๋‹น ์‹คํ–‰ ๊ฒฐ๊ณผ๋ฅผ ์‚ฝ์ž…ํ•˜์—ฌ ๋Œ€๋‹ต์„ ๋งŒ๋“ค์–ด๋‚ด๊ณ , ์ด ๋Œ€๋‹ต์„ ์‚ฌ์šฉ์ž์—๊ฒŒ responseํ•ฉ๋‹ˆ๋‹ค. ์ด API๋Š” ์ž์œ ๋กญ๊ฒŒ ์›ํ•˜๋Š” API๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. ์˜ˆ์ œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ๋Š” ์ฃผ๋กœ ์›น ํฌ๋กค๋ง์„ ์ด์šฉํ•˜์—ฌ API๋ฅผ ๊ตฌ์„ฑํ•˜์˜€๊ณ , ํฌ๋กค๋Ÿฌ ๊ตฌํ˜„ ์•„ํ‚คํ…์ฒ˜์— ๋Œ€ํ•ด์„œ๋„ ํ›„์ˆ ํ•˜๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

Slot Filling ๋ฐฉ์‹์˜ ์ฑ—๋ด‡์€ ์œ„์™€ ๊ฐ™์€ ํ๋ฆ„์œผ๋กœ ์ง„ํ–‰๋ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์ด๋Ÿฌํ•œ ๋ฐฉ์‹์˜ ์ฑ—๋ด‡์„ ๊ตฌํ˜„ํ•˜๋ ค๋ฉด ์ตœ์†Œํ•œ 3๊ฐ€์ง€์˜ ๋ชจ๋“ˆ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ์ฒซ๋ฒˆ์งธ๋กœ ์ธํ…ํŠธ ๋ถ„๋ฅ˜๋ชจ๋ธ, ์—”ํ‹ฐํ‹ฐ ์ธ์‹๋ชจ๋ธ, ๊ทธ๋ฆฌ๊ณ  ๋Œ€๋‹ต ์ƒ์„ฑ๋ชจ๋“ˆ(์˜ˆ์ œ์—์„œ๋Š” ํฌ๋กค๋ง)์ž…๋‹ˆ๋‹ค. Kochat์€ ์ด ์„ธ๊ฐ€์ง€ ๋ชจ๋“ˆ๊ณผ ์ด๋ฅผ ์„œ๋น™ํ•  Restful API๊นŒ์ง€ ๋ชจ๋‘ ํฌํ•จํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ด์— ๋Œ€ํ•ด์„œ๋Š” ์•„๋ž˜์˜ Usage ์ฑ•ํ„ฐ์—์„œ ๊ฐ๊ฐ ๋ชจ๋ธ์ด ์–ด๋–ป๊ฒŒ ๊ตฌํ˜„๋˜์–ด ์žˆ๋Š”์ง€ ์ž์„ธํžˆ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค.




3. Getting Started

3.1. Requirements

Kochat์„ ์ด์šฉํ•˜๋ ค๋ฉด ๋ฐ˜๋“œ์‹œ ๋ณธ์ธ์˜ OS์™€ ๋จธ์‹ ์— ๋งž๋Š” Pytorch๊ฐ€ ์„ค์น˜ ๋˜์–ด์žˆ์–ด์•ผํ•ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ Pytorch๋ฅผ ์„ค์น˜ํ•˜์ง€ ์•Š์œผ์…จ๋‹ค๋ฉด ์—ฌ๊ธฐ ์—์„œ ๋‹ค์šด๋กœ๋“œ ๋ฐ›์•„์ฃผ์„ธ์š”. (Kochat์„ ์„ค์น˜ํ•œ๋‹ค๊ณ  ํ•ด์„œ Pytorch๊ฐ€ ํ•จ๊ป˜ ์„ค์น˜๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋ณธ์ธ ๋ฒ„์ „์— ๋งž๋Š” Pytorch๋ฅผ ๋‹ค์šด๋กœ๋“œ ๋ฐ›์•„์ฃผ์„ธ์š”)


3.2. pip install

pip๋ฅผ ์ด์šฉํ•ด Kochat์„ ๊ฐ„๋‹จํ•˜๊ฒŒ ๋‹ค์šด๋กœ๋“œํ•˜๊ณ  ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์•„๋ž˜ ๋ช…๋ น์–ด๋ฅผ ํ†ตํ•ด์„œ kochat์„ ๋‹ค์šด๋กœ๋“œ ๋ฐ›์•„์ฃผ์„ธ์š”.

pip install kochat

3.3 Dependencies

ํŒจํ‚ค์ง€๋ฅผ ๊ตฌํ˜„ํ•˜๋Š”๋ฐ ์‚ฌ์šฉ๋œ ๋””ํŽœ๋˜์‹œ๋Š” ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค. (Kochat ์„ค์น˜์‹œ ํ•จ๊ป˜ ์„ค์น˜๋ฉ๋‹ˆ๋‹ค.)

matplotlib==3.2.1
pandas==1.0.4
gensim==3.8.3
konlpy==0.5.2
numpy==1.18.5
joblib==0.15.1
scikit-learn==0.23.1
pytorch-crf==0.7.2
requests==2.24.0
flask==1.1.2

3.4 Configuration ํŒŒ์ผ ์ถ”๊ฐ€ํ•˜๊ธฐ

pip๋ฅผ ์ด์šฉํ•ด Kochat์„ ๋‹ค์šด๋กœ๋“œ ๋ฐ›์•˜๋‹ค๋ฉด ํ”„๋กœ์ ํŠธ์—, kochat์˜ configuration ํŒŒ์ผ์„ ์ถ”๊ฐ€ํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค. kochat_config.zip ์„ ๋‹ค์šด๋กœ๋“œ ๋ฐ›๊ณ  ์••์ถ•์„ ํ’€์–ด์„œ interpreter์˜ working directory์— ๋„ฃ์Šต๋‹ˆ๋‹ค. (kochat api๋ฅผ ์‹คํ–‰ํ•˜๋Š” ํŒŒ์ผ๊ณผ ๋™์ผํ•œ ๊ฒฝ๋กœ์— ์žˆ์–ด์•ผํ•ฉ๋‹ˆ๋‹ค. ์ž์„ธํ•œ ์˜ˆ์‹œ๋Š” ์•„๋ž˜ ๋ฐ๋ชจ์—์„œ ํ™•์ธํ•˜์‹ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.) config ํŒŒ์ผ์—๋Š” ๋‹ค์–‘ํ•œ ์„ค์ • ๊ฐ’๋“ค์ด ์กด์žฌํ•˜๋‹ˆ ํ™•์ธํ•˜๊ณ  ์ž…๋ง›๋Œ€๋กœ ๋ณ€๊ฒฝํ•˜์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค.


3.5 ๋ฐ์ดํ„ฐ์…‹ ๋„ฃ๊ธฐ

์ด์ œ ์—ฌ๋Ÿฌ๋ถ„์ด ํ•™์Šต์‹œํ‚ฌ ๋ฐ์ดํ„ฐ์…‹์„ ๋„ฃ์–ด์•ผํ•ฉ๋‹ˆ๋‹ค. ๊ทธ ์ „์— ๋ฐ์ดํ„ฐ์…‹์˜ ํฌ๋งท์— ๋Œ€ํ•ด์„œ ๊ฐ„๋‹จํ•˜๊ฒŒ ์•Œ์•„๋ด…์‹œ๋‹ค. Kochat์€ ๊ธฐ๋ณธ์ ์œผ๋กœ Slot filling์„ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•˜๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— Intent์™€ Entity ๋ฐ์ดํ„ฐ์…‹์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ด ๋‘๊ฐ€์ง€ ๋ฐ์ดํ„ฐ์…‹์„ ๋”ฐ๋กœ ๋งŒ๋“ค๋ฉด ์ƒ๋‹นํžˆ ๋ฒˆ๊ฑฐ๋กœ์›Œ์ง€๊ธฐ ๋•Œ๋ฌธ์— ํ•œ๊ฐ€์ง€ ํฌ๋งท์œผ๋กœ ๋‘๊ฐ€์ง€ ๋ฐ์ดํ„ฐ๋ฅผ ์ž๋™์œผ๋กœ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ์•„๋ž˜ ๋ฐ์ดํ„ฐ์…‹ ๊ทœ์น™๋“ค์— ๋งž์ถฐ์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์ƒ์„ฑํ•ด์ฃผ์„ธ์š”

3.5.1. ๋ฐ์ดํ„ฐ ํฌ๋งท

๊ธฐ๋ณธ์ ์œผ๋กœ intent์™€ entity๋ฅผ ๋‚˜๋ˆ„๋ ค๋ฉด, ๋‘๊ฐ€์ง€๋ฅผ ๋ชจ๋‘ ๊ตฌ๋ถ„ํ•  ์ˆ˜ ์žˆ์–ด์•ผํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์„ ํƒํ•œ ๋ฐฉ์‹์€ ์ธํ…ํŠธ๋Š” ํŒŒ์ผ๋กœ ๊ตฌ๋ถ„, ์—”ํ‹ฐํ‹ฐ๋Š” ๋ผ๋ฒจ๋กœ ๊ตฌ๋ถ„ํ•˜๋Š” ๊ฒƒ์ด์˜€์Šต๋‹ˆ๋‹ค. ์ถ”ํ›„ ๋ฆด๋ฆฌ์ฆˆ ๋ฒ„์ „์—์„œ๋Š” Rasa์ฒ˜๋Ÿผ ํ›จ์”ฌ ์‰ฌ์šด ๋ฐฉ์‹์œผ๋กœ ๋ณ€๊ฒฝํ•˜๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค๋งŒ, ์ดˆ๊ธฐ๋ฒ„์ „์—์„œ๋Š” ๋‹ค์†Œ ๋ถˆํŽธํ•˜๋”๋ผ๋„ ์•„๋ž˜์˜ ํฌ๋งท์„ ๋”ฐ๋ผ์ฃผ์‹œ๊ธธ ๋ฐ”๋ž๋‹ˆ๋‹ค.

  • weather.csv
question,label
๋‚ ์”จ ์•Œ๋ ค์ฃผ์„ธ์š”,O O
์›”์š”์ผ ์ธ์ œ ๋น„์˜ค๋‹ˆ,S-DATE S-LOCATION O
๊ตฐ์‚ฐ ๋‚ ์”จ ์ถ”์šธ๊นŒ ์ •๋ง,S-LOCATION O O O
๊ณก์„ฑ ๋น„์˜ฌ๊นŒ,S-LOCATION O
๋‚ด์ผ ๋‹จ์–‘ ๋ˆˆ ์˜ค๊ฒ ์ง€ ์•„๋งˆ,S-DATE S-LOCATION O O O
๊ฐ•์›๋„ ์ถ˜์ฒœ ๊ฐ€๋Š”๋ฐ ์˜ค๋Š˜ ๋‚ ์”จ ์•Œ๋ ค์ค˜,B-LOCATION E-LOCATION O S-DATE O O
์ „๋ถ ๊ตฐ์‚ฐ ๊ฐ€๋Š”๋ฐ ํ™”์š”์ผ ๋‚ ์”จ ์•Œ๋ ค์ค„๋ž˜,B-LOCATION E-LOCATION O S-DATE O O
์ œ์ฃผ ์„œ๊ท€ํฌ ๊ฐ€๋ ค๋Š”๋ฐ ํ™”์š”์ผ ๋‚ ์”จ ์•Œ๋ ค์ค˜,B-LOCATION E-LOCATION O S-DATE O O
์˜ค๋Š˜ ์ œ์ฃผ๋„ ๋‚ ์”จ ์•Œ๋ ค์ค˜,S-DATE S-LOCATION O O
... (์ƒ๋žต)
  • travel.csv
question,label
์–ด๋”” ๊ด€๊ด‘์ง€ ๊ฐ€๊ฒ ๋ƒ,O O O
ํŒŒ์ฃผ ์œ ๋ช…ํ•œ ๊ณต์—ฐ์žฅ ์•Œ๋ ค์ค˜,S-LOCATION O S-PLACE O
์ฐฝ์› ์—ฌํ–‰ ๊ฐˆ๋งŒํ•œ ๋ฐ”๋‹ค,S-LOCATION O O S-PLACE
ํ‰ํƒ ๊ฐˆ๋งŒํ•œ ์Šคํ‚ค์žฅ ์—ฌํ–‰ ํ•ด๋ณด๊ณ  ์‹ถ๋„ค,S-LOCATION O S-PLACE O O O
์ œ์ฃผ๋„ ํ…œํ”Œ์Šคํ…Œ์ด ์—ฌํ–‰ ๊ฐˆ ๋ฐ ์ถ”์ฒœํ•ด ์ค˜,S-LOCATION S-PLACE O O O O O
์ „์ฃผ ๊ฐ€๊นŒ์šด ๋ฐ”๋‹ค ๊ด€๊ด‘์ง€ ๋ณด์—ฌ์ค˜ ๋ด์š”,S-LOCATION O S-PLACE O O O
์šฉ์ธ ๊ฐ€๊นŒ์šด ์ถ•๊ตฌ์žฅ ์–ด๋”จ์–ด,S-LOCATION O S-PLACE O
๋ถ๋น„๋Š” ๊ด€๊ด‘์ง€,O O
์ฒญ์ฃผ ๊ฐ€์„ ํ’๊ฒฝ ์˜ˆ์œ ์‚ฐ ๊ฐ€๋ณด๊ณ  ์‹ถ์–ด,S-LOCATION S-DATE O O S-PLACE O O
... (์ƒ๋žต)

์œ„ ์ฒ˜๋Ÿผ question,label์ด๋ผ๋Š” ํ—ค๋”(์ปฌ๋Ÿผ๋ช…)์„ ๊ฐ€์žฅ ์œ—์ค„์— ์œ„์น˜์‹œํ‚ค๊ณ , ๊ทธ ์•„๋ž˜๋กœ ๋‘๊ฐœ์˜ ์ปฌ๋ฆผ question๊ณผ label์— ํ•ด๋‹นํ•˜๋Š” ๋‚ด์šฉ์„ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค. ๊ฐ ๋‹จ์–ด ๋ฐ ์—”ํ‹ฐํ‹ฐ๋Š” ๋„์–ด์“ฐ๊ธฐ๋กœ ๊ตฌ๋ถ„๋ฉ๋‹ˆ๋‹ค. ๋ฐ๋ชจ ๋ฐ์ดํ„ฐ๋Š” BIOํƒœ๊น…์„ ๊ฐœ์„ ํ•œ BIOESํƒœ๊น…์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ผ๋ฒจ๋งํ–ˆ๋Š”๋ฐ, ์—”ํ‹ฐํ‹ฐ ํƒœ๊น… ๋ฐฉ์‹์€ ์ž์œ ๋กญ๊ฒŒ ๊ณ ๋ฅด์…”๋„ ๋ฉ๋‹ˆ๋‹ค. (config์—์„œ ์„ค์ • ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.) ์—”ํ‹ฐํ‹ฐ ํƒœ๊น… ์Šคํ‚ค๋งˆ์— ๊ด€๋ จ๋œ ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์—ฌ๊ธฐ ๋ฅผ ์ฐธ๊ณ ํ•˜์„ธ์š”.


3.5.2. ๋ฐ์ดํ„ฐ์…‹ ์ €์žฅ ๊ฒฝ๋กœ

๋ฐ์ดํ„ฐ์…‹ ์ €์žฅ๊ฒฝ๋กœ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ configํŒŒ์ผ์ด ์žˆ๋Š” ๊ณณ์„ root๋กœ ์ƒ๊ฐํ–ˆ์„ ๋•Œ, "root/data/raw"์ž…๋‹ˆ๋‹ค. ์ด ๊ฒฝ๋กœ๋Š” config์˜ DATA ์ฑ•ํ„ฐ์—์„œ ๋ณ€๊ฒฝ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

root
  |_data
    |_raw
      |_weather.csv
      |_dust.csv
      |_retaurant.csv
      |_...

3.5.3. ์ธํ…ํŠธ ๋‹จ์œ„๋กœ ํŒŒ์ผ ๋ถ„ํ• 

๊ฐ ์ธํ…ํŠธ ๋‹จ์œ„๋กœ ํŒŒ์ผ์„ ๋ถ„ํ• ํ•ฉ๋‹ˆ๋‹ค. ์ด ๋•Œ, ํŒŒ์ผ๋ช…์ด ์ธํ…ํŠธ๋ช…์ด ๋ฉ๋‹ˆ๋‹ค. ํŒŒ์ผ๋ช…์€ ํ•œ๊ธ€๋กœ ํ•ด๋„ ์ƒ๊ด€ ์—†๊ธด ํ•˜์ง€๋งŒ, ๋ฆฌ๋ˆ…์Šค ์šด์˜์ฒด์ œ์˜ ๊ฒฝ์šฐ ์‹œ๊ฐํ™”์‹œ matplotlib์— ํ•œ๊ธ€ํฐํŠธ๊ฐ€ ์„ค์น˜๋˜์–ด์žˆ์ง€ ์•Š๋‹ค๋ฉด ๊ธ€์ž๊ฐ€ ๊นจ์ง€๋‹ˆ, ๊ฐ€๊ธ‰์ ์ด๋ฉด ์‹œ๊ฐํ™”๋ฅผ ์œ„ํ•ด ์˜์–ด๋กœ ํ•˜๋Š” ๊ฒƒ์„ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค. (๋งŒ์•ฝ ๊ธ€์ž๊ฐ€ ๊นจ์ง€์ง€ ์•Š์œผ๋ฉด ํ•œ๊ธ€๋กœ ํ•ด๋„ ๋ฌด๋ฐฉํ•˜๋‹ˆ, ํ•œ๊ธ€๋กœ ํ•˜๋ ค๋ฉด ํฐํŠธ๋ฅผ ์„ค์น˜ํ•ด์ฃผ์„ธ์š”.)

root
  |_data
    |_raw
      |_weather.csv      โ† intent : weather
      |_dust.csv         โ† intent : dust
      |_retaurant.csv    โ† intent : restaurant
      |_...

3.5.4. ํŒŒ์ผ์˜ ํ—ค๋”(์ปฌ๋Ÿผ๋ช…) ์„ค์ •

ํŒŒ์ผ์˜ ํ—ค๋”(์ปฌ๋Ÿผ๋ช…)์€ ๋ฐ˜๋“œ์‹œ question๊ณผ label๋กœ ํ•ด์ฃผ์„ธ์š”. ํ—ค๋”๋ฅผ config์—์„œ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ๊ฒŒ ํ• ๊นŒ๋„ ์ƒ๊ฐํ–ˆ์ง€๋งŒ, ๋ณ„๋กœ ํฐ ์˜๋ฏธ๊ฐ€ ์—†๋Š” ๊ฒƒ ๊ฐ™์•„์„œ ์šฐ์„ ์€ ๊ณ ์ •๋œ ๊ฐ’์ธ question๊ณผ label๋กœ ์„ค์ •ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

question,label โ† ์ค‘์š” !!!
... (์ƒ๋žต)

3.5.5. ๋ผ๋ฒจ๋ง ์‹ค์ˆ˜ ๊ฒ€์ถœ

์ƒ˜ํ”Œ ๋‹น question์˜ ๋‹จ์–ด ๊ฐฏ์ˆ˜์™€ label์˜ ์—”ํ‹ฐํ‹ฐ ๊ฐฏ์ˆ˜๋Š” ๋™์ผํ•ด์•ผํ•˜๋ฉฐ config์— ์ •์˜ํ•œ ์—”ํ‹ฐํ‹ฐ๋งŒ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ๋ผ๋ฒจ๋ง ์‹ค์ˆ˜๋Š” Kochat์ด ๋ฐ์ดํ„ฐ๋ฅผ ๋ณ€ํ™˜ํ• ๋•Œ ๊ฒ€์ถœํ•ด์„œ ์–ด๋””๊ฐ€ ํ‹€๋ ธ๋Š”์ง€ ์•Œ๋ ค์ค๋‹ˆ๋‹ค.

case 1: ๋ผ๋ฒจ๋ง ๋งค์นญ ์‹ค์ˆ˜ ๋ฐฉ์ง€


question = ์ „์ฃผ ๋ˆˆ ์˜ฌ๊นŒ (size : 3)
label = S-LOCATION O O O (size : 4)

โ†’ ์—๋Ÿฌ ๋ฐœ์ƒ! (question๊ณผ label์˜ ์ˆ˜๊ฐ€ ๋‹ค๋ฆ„)
case 2: ๋ผ๋ฒจ๋ง ์˜คํƒ€ ๋ฐฉ์ง€


(in kochat_config.py)
DATA = {
    ... (์ƒ๋žต)

    'NER_categories': ['DATE', 'LOCATION', 'RESTAURANT', 'PLACE'],  # ์‚ฌ์šฉ์ž ์ •์˜ ํƒœ๊ทธ
    'NER_tagging': ['B', 'E', 'I', 'S'],  # NER์˜ BEGIN, END, INSIDE, SINGLE ํƒœ๊ทธ
    'NER_outside': 'O',  # NER์˜ Oํƒœ๊ทธ (Outside๋ฅผ ์˜๋ฏธ)
}

question = ์ „์ฃผ ๋ˆˆ ์˜ฌ๊นŒ
label = Z-LOC O O

โ†’ ์—๋Ÿฌ ๋ฐœ์ƒ! (์ •์˜๋˜์ง€ ์•Š์€ ์—”ํ‹ฐํ‹ฐ : Z-LOC)
NER_tagging + '-' + NER_categories์˜ ํ˜•ํƒœ๊ฐ€ ์•„๋‹ˆ๋ฉด ์—๋Ÿฌ๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

3.5.6. OOD ๋ฐ์ดํ„ฐ์…‹

OOD๋ž€ Out of distribution์˜ ์•ฝ์ž๋กœ, ๋ถ„ํฌ ์™ธ ๋ฐ์ดํ„ฐ์…‹์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. ์ฆ‰, ํ˜„์žฌ ์ฑ—๋ด‡์ด ์ง€์›ํ•˜๋Š” ๊ธฐ๋Šฅ ์ด์™ธ์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. OOD ๋ฐ์ดํ„ฐ์…‹์ด ์—†์–ด๋„ Kochat์„ ์ด์šฉํ•˜๋Š”๋ฐ์—๋Š” ์•„๋ฌด๋Ÿฐ ๋ฌธ์ œ๊ฐ€ ์—†์ง€๋งŒ, OOD ๋ฐ์ดํ„ฐ์…‹์„ ๊ฐ–์ถ”๋ฉด ๋งค์šฐ ๊ท€์ฐฎ์€ ๋ช‡๋ช‡ ๋ถ€๋ถ„๋“ค์„ ํšจ๊ณผ์ ์œผ๋กœ ์ž๋™ํ™” ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. (์ฃผ๋กœ Fallback Detection threshold ์„ค์ •) OOD ๋ฐ์ดํ„ฐ์…‹์€ ์•„๋ž˜์ฒ˜๋Ÿผ "root/data/ood"์— ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

root
  |_data
    |_raw
      |_weather.csv      
      |_dust.csv         
      |_retaurant.csv
      |_...
    |_ood
      |_ood_data_1.csv    โ† data/oodํด๋”์— ์œ„์น˜ํ•˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.
      |_ood_data_2.csv    โ† data/oodํด๋”์— ์œ„์น˜ํ•˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.

OOD ๋ฐ์ดํ„ฐ์…‹์€ ์•„๋ž˜์™€ ๊ฐ™์ด question๊ณผ OOD์˜ ์˜๋„๋กœ ๋ผ๋ฒจ๋งํ•ฉ๋‹ˆ๋‹ค. ๋ฐ๋ชจ ๋ฐ์ดํ„ฐ์…‹์€ ์ „๋ถ€ ์˜๋„๋Œ€๋กœ ๋ผ๋ฒจ๋งํ–ˆ์ง€๋งŒ, ์ด ์˜๋„๊ฐ’์„ ์‚ฌ์šฉํ•˜์ง„ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ๊ทธ๋ƒฅ ์•„๋ฌด๊ฐ’์œผ๋กœ๋‚˜ ๋ผ๋ฒจ๋งํ•ด๋„ ์‚ฌ์‹ค ๋ฌด๊ด€ํ•ฉ๋‹ˆ๋‹ค.

๋ฐ๋ชจ_ood_๋ฐ์ดํ„ฐ.csv

question,label
์ตœ๊ทผ ์žˆ๋˜์ผ ์ตœ๊ทผ ์ด์Šˆ ์•Œ๋ ค์ค˜,๋‰ด์Šค์ด์Šˆ
์ตœ๊ทผ ํ•ซํ–ˆ๋˜ ๊ฒƒ ์•Œ๋ ค์ค˜,๋‰ด์Šค์ด์Šˆ
๋‚˜ํ•œํ…Œ ์ข‹์€ ๋ช…์–ธํ•ด์ค„ ์ˆ˜ ์žˆ๋ƒ,๋ช…์–ธ
๋‚˜ ์ข‹์€ ๋ช…์–ธ ์ข€ ๋“ค๋ ค์ฃผ๋ผ,๋ช…์–ธ
์ข‹์€ ๋ช…์–ธ ์ข€ ํ•ด๋ด,๋ช…์–ธ
๋ฐฑ์žฌ๋ฒ” ๋…ธ๋ž˜ ๋“ค์„๋ž˜์š”,์Œ์•…
๋น„ ๋…ธ๋ž˜ ๊นก ๋“ฃ๊ณ  ์‹ถ๋‹ค,์Œ์•…
์˜ํ™” ost ์ถ”์ฒœํ•ด์ค˜,์Œ์•…
์ง€๊ธˆ ์‹œ๊ฐ„ ์ข€ ์•Œ๋ ค๋‹ฌ๋ผ๊ณ ,๋‚ ์งœ์‹œ๊ฐ„
์ง€๊ธˆ ์‹œ๊ฐ„ ์ข€ ์•Œ๋ ค์ค˜,๋‚ ์งœ์‹œ๊ฐ„
์ง€๊ธˆ ๋ช‡ ์‹œ ๋ช‡ ๋ถ„์ธ์ง€ ์•„๋‹ˆ,๋‚ ์งœ์‹œ๊ฐ„
๋ช…์ ˆ ์ŠคํŠธ๋ ˆ์Šค ใ…œใ…œ,์žก๋‹ด
๋ญํ•˜๊ณ  ๋†€์ง€ ใ…Žใ…Ž,์žก๋‹ด
๋‚˜๋ž‘ ๋†€์•„์ฃผ๋ผ ์ข€,์žก๋‹ด
๋ญํ•˜๊ณ  ์‚ด์ง€,์žก๋‹ด
... (์ƒ๋žต)

์ด๋ ‡๊ฒŒ ๋ผ๋ฒจ๋ง ํ•ด๋„ ๋˜์ง€๋งŒ ์–ด์ฐจํ”ผ ๋ผ๋ฒจ ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์•„๋ž˜์ฒ˜๋Ÿผ ๋ผ๋ฒจ๋งํ•ด๋„ ๋ฌด๊ด€ํ•ฉ๋‹ˆ๋‹ค.

๋ฐ๋ชจ_ood_๋ฐ์ดํ„ฐ.csv

question,label
์ตœ๊ทผ ์žˆ๋˜์ผ ์ตœ๊ทผ ์ด์Šˆ ์•Œ๋ ค์ค˜,OOD
์ตœ๊ทผ ํ•ซํ–ˆ๋˜ ๊ฒƒ ์•Œ๋ ค์ค˜,OOD
๋‚˜ํ•œํ…Œ ์ข‹์€ ๋ช…์–ธํ•ด์ค„ ์ˆ˜ ์žˆ๋ƒ,OOD
๋‚˜ ์ข‹์€ ๋ช…์–ธ ์ข€ ๋“ค๋ ค์ฃผ๋ผ,OOD
์ข‹์€ ๋ช…์–ธ ์ข€ ํ•ด๋ด,OOD
๋ฐฑ์žฌ๋ฒ” ๋…ธ๋ž˜ ๋“ค์„๋ž˜์š”,OOD
๋น„ ๋…ธ๋ž˜ ๊นก ๋“ฃ๊ณ  ์‹ถ๋‹ค,OOD
์˜ํ™” ost ์ถ”์ฒœํ•ด์ค˜,OOD
์ง€๊ธˆ ์‹œ๊ฐ„ ์ข€ ์•Œ๋ ค๋‹ฌ๋ผ๊ณ ,OOD
์ง€๊ธˆ ์‹œ๊ฐ„ ์ข€ ์•Œ๋ ค์ค˜,OOD
์ง€๊ธˆ ๋ช‡ ์‹œ ๋ช‡ ๋ถ„์ธ์ง€ ์•„๋‹ˆ,OOD
๋ช…์ ˆ ์ŠคํŠธ๋ ˆ์Šค ใ…œใ…œ,OOD
๋ญํ•˜๊ณ  ๋†€์ง€ ใ…Žใ…Ž,OOD
๋‚˜๋ž‘ ๋†€์•„์ฃผ๋ผ ์ข€,OOD
๋ญํ•˜๊ณ  ์‚ด์ง€,OOD
... (์ƒ๋žต)

OOD ๋ฐ์ดํ„ฐ๋Š” ๋ฌผ๋ก  ๋งŽ์œผ๋ฉด ์ข‹๊ฒ ์ง€๋งŒ ๋งŒ๋“œ๋Š” ๊ฒƒ ์ž์ฒด๊ฐ€ ๋ถ€๋‹ด์ด๊ธฐ ๋•Œ๋ฌธ์— ์ ์€ ์ˆ˜๋งŒ ๋„ฃ์–ด๋„ ๋ฉ๋‹ˆ๋‹ค. ๋ฐ๋ชจ ๋ฐ์ดํ„ฐ์˜ ๊ฒฝ์šฐ๋Š” ์ด 3000๋ผ์ธ์˜ ๋ฐ์ดํ„ฐ ์ค‘ 600๋ผ์ธ์ •๋„์˜ OOD ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฝ์ž…ํ•˜์˜€์Šต๋‹ˆ๋‹ค. ๋ฐ์ดํ„ฐ๊นŒ์ง€ ๋ชจ๋‘ ์‚ฝ์ž…ํ•˜์…จ๋‹ค๋ฉด kochat์„ ์ด์šฉํ•  ์ค€๋น„๊ฐ€ ๋๋‚ฌ์Šต๋‹ˆ๋‹ค. ์•„๋ž˜ ์ฑ•ํ„ฐ์—์„œ๋Š” ์ž์„ธํ•œ ์‚ฌ์šฉ๋ฒ•์— ๋Œ€ํ•ด ์•Œ๋ ค๋“œ๋ฆฌ๊ฒ ์Šต๋‹ˆ๋‹ค.


4. Usage

4.1. from kochat.data

kochat.data ํŒจํ‚ค์ง€์—๋Š” Dataset ํด๋ž˜์Šค๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. Datasetํด๋ž˜์Šค๋Š” ๋ถ„๋ฆฌ๋œ raw ๋ฐ์ดํ„ฐ ํŒŒ์ผ๋“ค์„ ํ•˜๋‚˜๋กœ ํ•ฉ์ณ์„œ ํ†ตํ•ฉ intentํŒŒ์ผ๊ณผ ํ†ตํ•ฉ entityํŒŒ์ผ๋กœ ๋งŒ๋“ค๊ณ , embedding, intent, entity, inference์— ๊ด€๋ จ๋œ ๋ฐ์ดํ„ฐ์…‹์„ ๋ฏธ๋‹ˆ๋ฐฐ์น˜๋กœ ์ž˜๋ผ์„œ pytorch์˜ DataLoaderํ˜•ํƒœ๋กœ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ ๋ชจ๋ธ, Loss ํ•จ์ˆ˜ ๋“ฑ์„ ์ƒ์„ฑํ•  ๋•Œ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ž…๋ ฅํ•˜๋Š” label_dict๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. Dataset ํด๋ž˜์Šค๋ฅผ ์ƒ์„ฑํ•  ๋•Œ ํ•„์š”ํ•œ ํŒŒ๋ผ๋ฏธํ„ฐ์ธ ood๋Š” OOD ๋ฐ์ดํ„ฐ์…‹ ์‚ฌ์šฉ ์—ฌ๋ถ€์ž…๋‹ˆ๋‹ค. True๋กœ ์„ค์ •ํ•˜๋ฉด ood ๋ฐ์ดํ„ฐ์…‹์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.


  • Dataset ๊ธฐ๋Šฅ 1. ๋ฐ์ดํ„ฐ์…‹ ์ƒ์„ฑ
from kochat.data import Dataset


# ํด๋ž˜์Šค ์ƒ์„ฑ์‹œ rawํŒŒ์ผ๋“ค์„ ๊ฒ€์ฆํ•˜๊ณ  ํ†ตํ•ฉํ•ฉ๋‹ˆ๋‹ค.
dataset = Dataset(ood=True, naver_fix=True)  

# ์ž„๋ฒ ๋”ฉ ๋ฐ์ดํ„ฐ์…‹ ์ƒ์„ฑ
embed_dataset = dataset.load_embed() 

# ์ธํ…ํŠธ ๋ฐ์ดํ„ฐ์…‹ ์ƒ์„ฑ (์ž„๋ฒ ๋”ฉ ํ”„๋กœ์„ธ์„œ ํ•„์š”)
intent_dataset = dataset.load_intent(emb) 

# ์—”ํ‹ฐํ‹ฐ ๋ฐ์ดํ„ฐ์…‹ ์ƒ์„ฑ (์ž„๋ฒ ๋”ฉ ํ”„๋กœ์„ธ์„œ ํ•„์š”)
entity_dataset = dataset.load_entity(emb) 

# ์ถ”๋ก ์šฉ ๋ฐ์ดํ„ฐ์…‹ ์ƒ์„ฑ (์ž„๋ฒ ๋”ฉ ํ”„๋กœ์„ธ์„œ ํ•„์š”)
predict_dataset = dataset.load_predict("์„œ์šธ ๋ง›์ง‘ ์ถ”์ฒœํ•ด์ค˜", emb) 

  • Dataset ๊ธฐ๋Šฅ 2. ๋ผ๋ฒจ ๋”•์…”๋„ˆ๋ฆฌ ์ƒ์„ฑ
from kochat.data import Dataset


# ํด๋ž˜์Šค ์ƒ์„ฑ์‹œ rawํŒŒ์ผ๋“ค์„ ๊ฒ€์ฆํ•˜๊ณ  ํ†ตํ•ฉํ•ฉ๋‹ˆ๋‹ค.
dataset = Dataset(ood=True, naver_fix=True)  

# ์ธํ…ํŠธ ๋ผ๋ฒจ ๋”•์…”๋„ˆ๋ฆฌ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
intent_dict = dataset.intent_dict 

# ์—”ํ‹ฐํ‹ฐ ๋ผ๋ฒจ ๋”•์…”๋„ˆ๋ฆฌ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
entity_dict = dataset.entity_dict

โš  Warning

Datasetํด๋ž˜์Šค๋Š” ์ „์ฒ˜๋ฆฌ์‹œ ํ† ํฐํ™”๋ฅผ ์ˆ˜ํ–‰ํ•  ๋•Œ, ํ•™์Šต/ํ…Œ์ŠคํŠธ ๋ฐ์ดํ„ฐ๋Š” ๋„์–ด์“ฐ๊ธฐ๋ฅผ ๊ธฐ์ค€์œผ๋กœ ํ† ํฐํ™”๋ฅผ ์ˆ˜ํ–‰ํ•˜๊ณ , ์‹ค์ œ ์‚ฌ์šฉ์ž์˜ ์ž…๋ ฅ์— ์ถ”๋ก ํ•  ๋•Œ๋Š” ๋„ค์ด๋ฒ„ ๋งž์ถค๋ฒ• ๊ฒ€์‚ฌ๊ธฐ์™€ Konlpy ํ† ํฌ๋‚˜์ด์ €๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ† ํฐํ™”๋ฅผ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. ๋„ค์ด๋ฒ„ ๋งž์ถค๋ฒ• ๊ฒ€์‚ฌ๊ธฐ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์„ฑ๋Šฅ์€ ๋”์šฑ ํ–ฅ์ƒ๋˜๊ฒ ์ง€๋งŒ, ์ƒ์—…์ ์œผ๋กœ ์ด์šฉ์‹œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๊ณ , ์ด์— ๋Œ€ํ•ด ๊ฐœ๋ฐœ์ž๋Š” ์–ด๋– ํ•œ ์ฑ…์ž„๋„ ์ง€์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๋งŒ์•ฝ Kochat์„ ์ƒ์—…์ ์œผ๋กœ ์ด์šฉํ•˜์‹œ๋ ค๋ฉด Dataset ์ƒ์„ฑ์‹œ naver_fixํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ False๋กœ ์„ค์ •ํ•ด์ฃผ์‹œ๊ธธ ๋ฐ”๋ž๋‹ˆ๋‹ค. False ์„ค์ •์‹œ์—๋Š” Konlpy ํ† ํฐํ™”๋งŒ ์ˆ˜ํ–‰ํ•˜๋ฉฐ, ์ถ”ํ›„ ๋ฒ„์ „์—์„œ๋Š” ๋„ค์ด๋ฒ„ ๋งž์ถค๋ฒ• ๊ฒ€์‚ฌ๊ธฐ๋ฅผ ์ž์ฒด์ ์ธ ๋„์–ด์“ฐ๊ธฐ ๊ฒ€์‚ฌ๋ชจ๋“ˆ ๋“ฑ์œผ๋กœ ๊ต์ฒดํ•  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.


4.2. from kochat.model

model ํŒจํ‚ค์ง€๋Š” ์‚ฌ์ „ ์ •์˜๋œ ๋‹ค์–‘ํ•œ built-in ๋ชจ๋ธ๋“ค์ด ์ €์žฅ๋œ ํŒจํ‚ค์ง€์ž…๋‹ˆ๋‹ค. ํ˜„์žฌ ๋ฒ„์ „์—์„œ๋Š” ์•„๋ž˜ ๋ชฉ๋ก์— ํ•ด๋‹นํ•˜๋Š” ๋ชจ๋ธ๋“ค์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ์ถ”ํ›„ ๋ฒ„์ „์ด ์—…๋ฐ์ดํŠธ ๋˜๋ฉด ์ง€๊ธˆ๋ณด๋‹ค ํ›จ์”ฌ ๋‹ค์–‘ํ•œ built-in ๋ชจ๋ธ์„ ์ง€์›ํ•  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค. ์•„๋ž˜ ๋ชฉ๋ก์„ ์ฐธ๊ณ ํ•˜์—ฌ ์‚ฌ์šฉํ•ด์ฃผ์‹œ๊ธธ ๋ฐ”๋ž๋‹ˆ๋‹ค.


4.2.1. embed ๋ชจ๋ธ

from kochat.model import embed


# 1. Gensim์˜ Word2Vec ๋ชจ๋ธ์˜ Wrapper์ž…๋‹ˆ๋‹ค.
# (OOV ํ† ํฐ์˜ ๊ฐ’์€ config์—์„œ ์„ค์ • ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.)
word2vec = embed.Word2Vec()

# 2. Gensim์˜ FastText ๋ชจ๋ธ์˜ Wrapper์ž…๋‹ˆ๋‹ค.
fasttext = embed.FastText()

4.2.2. intent ๋ชจ๋ธ

from kochat.model import intent


# 1. Residual Learning์„ ์ง€์›ํ•˜๋Š” 1D CNN์ž…๋‹ˆ๋‹ค.
cnn = intent.CNN(label_dict=dataset.intent_dict, residual=True)

# 2. Bidirectional์„ ์ง€์›ํ•˜๋Š” LSTM์ž…๋‹ˆ๋‹ค.
lstm = intent.LSTM(label_dict=dataset.intent_dict, bidirectional=True)

4.2.3. entity ๋ชจ๋ธ

from kochat.model import entity


# 1. Bidirectional์„ ์ง€์›ํ•˜๋Š” LSTM์ž…๋‹ˆ๋‹ค.
lstm = entity.LSTM(label_dict=dataset.entity_dict, bidirectional=True)

4.2.4. ์ปค์Šคํ…€ ๋ชจ๋ธ

Kochat์€ ์ปค์Šคํ…€ ๋ชจ๋ธ์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. Gensim์ด๋‚˜ Pytorch๋กœ ์ž‘์„ฑํ•œ ์ปค์Šคํ…€ ๋ชจ๋ธ์„ ์ง์ ‘ ํ•™์Šต์‹œํ‚ค๊ธฐ๊ณ  ์ฑ—๋ด‡ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๋งŒ์•ฝ ์ปค์Šคํ…€ ๋ชจ๋ธ์„ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ์•„๋ž˜์˜ ๋ช‡๊ฐ€์ง€ ๊ทœ์น™์„ ๋ฐ˜๋“œ์‹œ ๋”ฐ๋ผ์•ผํ•ฉ๋‹ˆ๋‹ค.

4.2.4.1. ์ปค์Šคํ…€ Gensim embed ๋ชจ๋ธ

์ž„๋ฒ ๋”ฉ์˜ ๊ฒฝ์šฐ ํ˜„์žฌ๋Š” Gensim ๋ชจ๋ธ๋งŒ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ์ถ”ํ›„์— Pytorch๋กœ ๋œ ์ž„๋ฒ ๋”ฉ ๋ชจ๋ธ(ELMO, BERT)๋“ฑ๋„ ์ง€์›ํ•  ๊ณ„ํš์ž…๋‹ˆ๋‹ค. Gensim Embedding ๋ชจ๋ธ์€ ์•„๋ž˜์™€ ๊ฐ™์€ ํ˜•ํƒœ๋กœ ๊ตฌํ˜„ํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค.

  1. @gensim ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ์„ค์ •
  2. BaseWordEmbeddingsModel๋ชจ๋ธ ์ค‘ ํ•œ ๊ฐ€์ง€ ์ƒ์†๋ฐ›๊ธฐ
  3. super().__init__()์— ํŒŒ๋ผ๋ฏธํ„ฐ ์‚ฝ์ž…ํ•˜๊ธฐ (self.XXX๋กœ ์ ‘๊ทผ๊ฐ€๋Šฅ)

from gensim.models import FastText
from kochat.decorators import gensim

# 1. @gensim ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ์„ค์ •ํ•˜๋ฉด 
# config์˜ GENSIM์— ์žˆ๋Š” ๋ชจ๋“  ๋ฐ์ดํ„ฐ์— ์ ‘๊ทผ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

@gensim
class FastText(FastText):
# 2. BaseWordEmbeddingsModel ๋ชจ๋ธ์ค‘ ํ•œ ๊ฐ€์ง€๋ฅผ  ์ƒ์†๋ฐ›์Šต๋‹ˆ๋‹ค.

    def __init__(self):
        # 3. `super().__init__()`์— ํ•„์š”ํ•œ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๋„ฃ์–ด์„œ ์ดˆ๊ธฐํ™”ํ•ด์ค๋‹ˆ๋‹ค.

        super().__init__(size=self.vector_size,
                         window=self.window_size,
                         workers=self.workers,
                         min_count=self.min_count,
                         iter=self.iter)



4.2.4.2. ์ปค์Šคํ…€ Intent ๋ชจ๋ธ

์ธํ…ํŠธ ๋ชจ๋ธ์€ torch๋กœ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค. ์ธํ…ํŠธ ๋ชจ๋ธ์—๋Š” self.label_dict ๊ฐ€ ๋ฐ˜๋“œ์‹œ ์กด์žฌํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ ์ตœ์ข… output ๋ ˆ์ด์–ด๋Š” ์ž๋™์ƒ์„ฑ๋˜๊ธฐ ๋•Œ๋ฌธ์— feature๋งŒ ์ถœ๋ ฅํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. ๋”์šฑ ์„ธ๋ถ€์ ์ธ ๊ทœ์น™์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  1. @intent ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ์„ค์ •
  2. torch.nn.Module ์ƒ์†๋ฐ›๊ธฐ
  3. ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ label_dict๋ฅผ ์ž…๋ ฅ๋ฐ›๊ณ  self.label_dict์— ํ• ๋‹นํ•˜๊ธฐ
  4. forward() ํ•จ์ˆ˜์—์„œ feature๋ฅผ [batch_size, -1] ๋กœ ๋งŒ๋“ค๊ณ  ๋ฆฌํ„ด

from torch import nn
from torch import Tensor
from kochat.decorators import intent
from kochat.model.layers.convolution import Convolution


# 1. @intent ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ์„ค์ •ํ•˜๋ฉด 
# config์˜ INTENT์— ์žˆ๋Š” ๋ชจ๋“  ์„ค์ •๊ฐ’์— ์ ‘๊ทผ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

@intent
class CNN(nn.Module):
# 2. torch.nn์˜ Module์„ ์ƒ์†๋ฐ›์Šต๋‹ˆ๋‹ค.

    def __init__(self, label_dict: dict, residual: bool = True):
        super(CNN, self).__init__()
        self.label_dict = label_dict
        # 3. intent๋ชจ๋ธ์€ ๋ฐ˜๋“œ์‹œ ์†์„ฑ์œผ๋กœ self.label_dict๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์–ด์•ผํ•ฉ๋‹ˆ๋‹ค.

        self.stem = Convolution(self.vector_size, self.d_model, kernel_size=1, residual=residual)
        self.hidden_layers = nn.Sequential(*[
            Convolution(self.d_model, self.d_model, kernel_size=1, residual=residual)
            for _ in range(self.layers)])

    def forward(self, x: Tensor) -> Tensor:
        x = x.permute(0, 2, 1)
        x = self.stem(x)
        x = self.hidden_layers(x)

        return x.view(x.size(0), -1)
        # 4. feature๋ฅผ [batch_size, -1]๋กœ ๋งŒ๋“ค๊ณ  ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
        # ์ตœ์ข… output ๋ ˆ์ด์–ด๋Š” kochat์ด ์ž๋™ ์ƒ์„ฑํ•˜๊ธฐ ๋•Œ๋ฌธ์— feature๋งŒ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค.
import torch
from torch import nn, autograd
from torch import Tensor
from kochat.decorators import intent


# 1. @intent ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ์„ค์ •ํ•˜๋ฉด 
# config์˜ INTENT์— ์žˆ๋Š” ๋ชจ๋“  ์„ค์ •๊ฐ’์— ์ ‘๊ทผ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

@intent
class LSTM(nn.Module):
# 2. torch.nn์˜ Module์„ ์ƒ์†๋ฐ›์Šต๋‹ˆ๋‹ค.
 
    def __init__(self, label_dict: dict, bidirectional: bool = True):

        super().__init__()
        self.label_dict = label_dict
        # 3. intent๋ชจ๋ธ์€ ๋ฐ˜๋“œ์‹œ ์†์„ฑ์œผ๋กœ self.label_dict๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์–ด์•ผํ•ฉ๋‹ˆ๋‹ค.

        self.direction = 2 if bidirectional else 1
        self.lstm = nn.LSTM(input_size=self.vector_size,
                            hidden_size=self.d_model,
                            num_layers=self.layers,
                            batch_first=True,
                            bidirectional=bidirectional)

    def init_hidden(self, batch_size: int) -> autograd.Variable:
        param1 = torch.randn(self.layers * self.direction, batch_size, self.d_model).to(self.device)
        param2 = torch.randn(self.layers * self.direction, batch_size, self.d_model).to(self.device)
        return autograd.Variable(param1), autograd.Variable(param2)

    def forward(self, x: Tensor) -> Tensor:
        b, l, v = x.size()
        out, (h_s, c_s) = self.lstm(x, self.init_hidden(b))

        # 4. feature๋ฅผ [batch_size, -1]๋กœ ๋งŒ๋“ค๊ณ  ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
        # ์ตœ์ข… output ๋ ˆ์ด์–ด๋Š” kochat์ด ์ž๋™ ์ƒ์„ฑํ•˜๊ธฐ ๋•Œ๋ฌธ์— feature๋งŒ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค.
        return h_s[0]



4.2.4.3. ์ปค์Šคํ…€ Entity ๋ชจ๋ธ

์—”ํ‹ฐํ‹ฐ ๋ชจ๋ธ๋„ ์—ญ์‹œ torch๋กœ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค. ์—”ํ‹ฐํ‹ฐ ๋ชจ๋ธ์—๋„ ์—ญ์‹œ self.label_dict ๊ฐ€ ๋ฐ˜๋“œ์‹œ ์กด์žฌํ•ด์•ผํ•˜๋ฉฐ, ๋˜ํ•œ ์ตœ์ข… output ๋ ˆ์ด์–ด๋Š” ์ž๋™์ƒ์„ฑ๋˜๊ธฐ ๋•Œ๋ฌธ์— feature๋งŒ ์ถœ๋ ฅํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. ๋”์šฑ ์„ธ๋ถ€์ ์ธ ๊ทœ์น™์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  1. @entity ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ์„ค์ •
  2. torch.nn.Module ์ƒ์†๋ฐ›๊ธฐ
  3. ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ label_dict๋ฅผ ์ž…๋ ฅ๋ฐ›๊ณ  self.label_dict์— ํ• ๋‹นํ•˜๊ธฐ
  4. forward() ํ•จ์ˆ˜์—์„œ feature๋ฅผ [batch_size, max_len, -1] ๋กœ ๋งŒ๋“ค๊ณ  ๋ฆฌํ„ด

import torch
from torch import nn, autograd
from torch import Tensor
from kochat.decorators import entity

# 1. @entity ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ์„ค์ •ํ•˜๋ฉด 
# config์˜ ENTITY์— ์žˆ๋Š” ๋ชจ๋“  ์„ค์ •๊ฐ’์— ์ ‘๊ทผ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

@entity
class LSTM(nn.Module):
# 2. torch.nn์˜ Module์„ ์ƒ์†๋ฐ›์Šต๋‹ˆ๋‹ค.
 
    def __init__(self, label_dict: dict, bidirectional: bool = True):

        super().__init__()
        self.label_dict = label_dict
        # 3. entity๋ชจ๋ธ์€ ๋ฐ˜๋“œ์‹œ ์†์„ฑ์œผ๋กœ self.label_dict๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์–ด์•ผํ•ฉ๋‹ˆ๋‹ค.

        self.direction = 2 if bidirectional else 1
        self.lstm = nn.LSTM(input_size=self.vector_size,
                            hidden_size=self.d_model,
                            num_layers=self.layers,
                            batch_first=True,
                            bidirectional=bidirectional)

    def init_hidden(self, batch_size: int) -> autograd.Variable:
        param1 = torch.randn(self.layers * self.direction, batch_size, self.d_model).to(self.device)
        param2 = torch.randn(self.layers * self.direction, batch_size, self.d_model).to(self.device)
        return torch.autograd.Variable(param1), torch.autograd.Variable(param2)

    def forward(self, x: Tensor) -> Tensor:
        b, l, v = x.size()
        out, _ = self.lstm(x, self.init_hidden(b))

        # 4. feature๋ฅผ [batch_size, max_len, -1]๋กœ ๋งŒ๋“ค๊ณ  ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
        # ์ตœ์ข… output ๋ ˆ์ด์–ด๋Š” kochat์ด ์ž๋™ ์ƒ์„ฑํ•˜๊ธฐ ๋•Œ๋ฌธ์— feature๋งŒ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค.
        return out




4.3. from kochat.proc

proc์€ Procssor์˜ ์ค„์ž„๋ง๋กœ, ๋‹ค์–‘ํ•œ ๋ชจ๋ธ๋“ค์˜ ํ•™์Šต/ํ…Œ์ŠคํŠธ์„ ์ˆ˜ํ–‰ํ•˜๋Š” ํ•จ์ˆ˜์ธ fit()๊ณผ ์ถ”๋ก ์„ ์ˆ˜ํ–‰ํ•˜๋Š” ํ•จ์ˆ˜์ธ predict() ๋“ฑ์„ ์ˆ˜ํ–‰ํ•˜๋Š” ํด๋ž˜์Šค ์ง‘ํ•ฉ์ž…๋‹ˆ๋‹ค. ํ˜„์žฌ ์ง€์›ํ•˜๋Š” ํ”„๋กœ์„ธ์„œ๋Š” ์ด 4๊ฐ€์ง€๋กœ ์•„๋ž˜์—์„œ ์ž์„ธํ•˜๊ฒŒ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค.

4.3.1. from kochat.proc import GensimEmbedder

GensimEmbedder๋Š” Gensim์˜ ์ž„๋ฒ ๋”ฉ ๋ชจ๋ธ์„ ํ•™์Šต์‹œํ‚ค๊ณ , ํ•™์Šต๋œ ๋ชจ๋ธ์„ ์‚ฌ์šฉํ•ด ๋ฌธ์žฅ์„ ์ž„๋ฒ ๋”ฉํ•˜๋Š” ํด๋ž˜์Šค์ž…๋‹ˆ๋‹ค. ์ž์„ธํ•œ ์‚ฌ์šฉ๋ฒ•์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

from kochat.data import Dataset
from kochat.proc import GensimEmbedder
from kochat.model import embed


dataset = Dataset(ood=True)

# ํ”„๋กœ์„ธ์„œ ์ƒ์„ฑ
emb = GensimEmbedder(
    model=embed.FastText()
)

# ๋ชจ๋ธ ํ•™์Šต
emb.fit(dataset.load_embed())

# ๋ชจ๋ธ ์ถ”๋ก  (์ž„๋ฒ ๋”ฉ)
user_input = emb.predict("์„œ์šธ ํ™๋Œ€ ๋ง›์ง‘ ์•Œ๋ ค์ค˜")



4.3.2. from kochat.proc import SoftmaxClassifier

SoftmaxClassifier๋Š” ๊ฐ€์žฅ ๊ธฐ๋ณธ์ ์ธ ๋ถ„๋ฅ˜ ํ”„๋กœ์„ธ์„œ์ž…๋‹ˆ๋‹ค. ์ด๋ฆ„์ด SoftmaxClassifier์ธ ์ด์œ ๋Š” Softmax Score๋ฅผ ์ด์šฉํ•ด Fallback Detection์„ ์ˆ˜ํ–‰ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ด๋ ‡๊ฒŒ ๋ช…๋ช…ํ•˜๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๋ช‡๋ช‡ ๋…ผ๋ฌธ ์—์„œ Calibrate๋˜์ง€ ์•Š์€ Softmax Score์„ ๋งˆ์น˜ Confidence์ฒ˜๋Ÿผ ์ฐฉ๊ฐํ•ด์„œ ์‚ฌ์šฉํ•˜๋ฉด ์‹ฌ๊ฐํ•œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ๋ณด์—ฌ์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค.

mnist


์œ„์˜ ๊ทธ๋ฆผ์€ MNIST ๋ถ„๋ฅ˜๋ชจ๋ธ์—์„œ 0.999 ์ด์ƒ์˜ Softmax Score๋ฅผ ๊ฐ€์ง€๋Š” ์ด๋ฏธ์ง€๋“ค์ž…๋‹ˆ๋‹ค. ์‹ค์ œ๋กœ 0 ~ 9๊นŒ์ง€์˜ ์ˆซ์ž์™€๋Š” ์ „ํ˜€ ์ƒ๊ด€์—†๋Š” ์ด๋ฏธ์ง€๋“ค์ด๊ธฐ ๋•Œ๋ฌธ์— ๋‚ฎ์€ Softmax Score๋ฅผ ๊ฐ€์งˆ ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐ๋˜์ง€๋งŒ ์‹ค์ œ๋กœ๋Š” ๊ทธ๋ ‡์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์‚ฌ์‹ค SoftmaxClassifier๋ฅผ ์‹ค์ œ ์ฑ—๋ด‡์˜ Intent Classification ๊ธฐ๋Šฅ์„ ์œ„ํ•ด ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์€ ์ ์ ˆํ•˜์ง€ ๋ชปํ•ฉ๋‹ˆ๋‹ค. SoftmaxClassifier๋Š” ์•„๋ž˜ ํ›„์ˆ ํ•  DistanceClassifier ์™€์˜ ์„ฑ๋Šฅ ๋น„๊ต๋ฅผ ์œ„ํ•ด ๊ตฌํ˜„ํ•˜์˜€์Šต๋‹ˆ๋‹ค. ์‚ฌ์šฉ๋ฒ•์€ ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

from kochat.data import Dataset
from kochat.proc import SoftmaxClassifier
from kochat.model import intent
from kochat.loss import CrossEntropyLoss


dataset = Dataset(ood=True)

# ํ”„๋กœ์„ธ์„œ ์ƒ์„ฑ
clf = SoftmaxClassifier(
    model=intent.CNN(dataset.intent_dict),
    loss=CrossEntropyLoss(dataset.intent_dict)
)

# ๋˜๋„๋ก์ด๋ฉด SoftmaxClassifier๋Š” CrossEntropyLoss๋ฅผ ์ด์šฉํ•ด์ฃผ์„ธ์š”
# ๋‹ค๋ฅธ Loss ํ•จ์ˆ˜๋“ค์€ ๊ฑฐ๋ฆฌ ๊ธฐ๋ฐ˜์˜ Metric Learning์„ ์ˆ˜ํ–‰ํ•˜๊ธฐ ๋•Œ๋ฌธ์— 
# Softmax Classifiaction์— ์ ์ ˆํ•˜์ง€ ๋ชปํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


# ๋ชจ๋ธ ํ•™์Šต
clf.fit(dataset.load_intent(emb))

# ๋ชจ๋ธ ์ถ”๋ก  (์ธํ…ํŠธ ๋ถ„๋ฅ˜)
clf.predict(dataset.load_predict("์˜ค๋Š˜ ์„œ์šธ ๋‚ ์”จ ์–ด๋–จ๊นŒ", emb))

4.3.3. from kochat.proc import DistanceClassifier

DistanceClassifier๋Š” SoftmaxClassifier์™€๋Š” ๋‹ค๋ฅด๊ฒŒ ๊ฑฐ๋ฆฌ๊ธฐ๋ฐ˜์œผ๋กœ ์ž‘๋™ํ•˜๋ฉฐ, ์ผ์ข…์˜ Memory Network์ž…๋‹ˆ๋‹ค. [batch_size, -1] ์˜ ์‚ฌ์ด์ฆˆ๋กœ ์ถœ๋ ฅ๋œ ์ถœ๋ ฅ๋ฒกํ„ฐ์™€ ๊ธฐ์กด ๋ฐ์ดํ„ฐ์…‹์— ์žˆ๋Š” ๋ฌธ์žฅ ๋ฒกํ„ฐ๋“ค ์‚ฌ์ด์˜ ๊ฑฐ๋ฆฌ๋ฅผ ๊ณ„์‚ฐํ•˜์—ฌ ๋ฐ์ดํ„ฐ์…‹์—์„œ ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด K๊ฐœ์˜ ์ƒ˜ํ”Œ์„ ์ฐพ๊ณ  ์ตœ๋‹ค ์ƒ˜ํ”Œ ํด๋ž˜์Šค๋กœ ๋ถ„๋ฅ˜ํ•˜๋Š” ์ตœ๊ทผ์ ‘ ์ด์›ƒ Retrieval ๊ธฐ๋ฐ˜์˜ ๋ถ„๋ฅ˜ ๋ชจ๋ธ์ž…๋‹ˆ๋‹ค.

์ด ๋•Œ ๋‹ค๋ฅธ ํด๋ž˜์Šค๋“ค์€ ๋ฉ€๋ฆฌ, ๊ฐ™์€ ํด๋ž˜์Šค๋ผ๋ฆฌ๋Š” ๊ฐ€๊นŒ์ด ์žˆ์–ด์•ผ ๋ถ„๋ฅ˜ํ•˜๊ธฐ์— ์ข‹๊ธฐ ๋•Œ๋ฌธ์— ์‚ฌ์šฉ์ž๊ฐ€ ์„ค์ •ํ•œ Lossํ•จ์ˆ˜(์ฃผ๋กœ Margin ๊ธฐ๋ฐ˜ Loss)๋ฅผ ์ ์šฉํ•ด Metric Learning์„ ์ˆ˜ํ–‰ํ•ด์„œ ํด๋ž˜์Šค ๊ฐ„์˜ Margin์„ ์ตœ๋Œ€์น˜๋กœ ๋ฒŒ๋ฆฌ๋Š” ๋ฉ”์ปค๋‹ˆ์ฆ˜์ด ๊ตฌํ˜„๋˜์–ด์žˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ์ตœ๊ทผ์ ‘ ์ด์›ƒ ์•Œ๊ณ ๋ฆฌ์ฆ˜์˜ K๊ฐ’์€ config์—์„œ ์ง์ ‘ ์ง€์ • ํ•  ์ˆ˜๋„ ์žˆ๊ณ  GridSearch๋ฅผ ์ ์šฉํ•˜์—ฌ ์ž๋™์œผ๋กœ ์ตœ์ ์˜ K๊ฐ’์„ ์ฐพ์„ ์ˆ˜ ์žˆ๊ฒŒ ์„ค๊ณ„ํ•˜์˜€์Šต๋‹ˆ๋‹ค.


์ตœ๊ทผ์ ‘ ์ด์›ƒ์„ ์ฐพ์„ ๋•Œ Brute force๋กœ ์ง์ ‘ ๊ฑฐ๋ฆฌ๋ฅผ ์ผ์ผ์ด ๋‹ค ๊ตฌํ•˜๋ฉด ๊ต‰์žฅํžˆ ๋Š๋ฆฌ๊ธฐ ๋•Œ๋ฌธ์— ๋‹ค์ฐจ์› ๊ฒ€์ƒ‰ํŠธ๋ฆฌ์ธ KDTree ํ˜น์€ BallTree (KDTree์˜ ๊ฐœ์„  ํ˜•ํƒœ)๋ฅผ ํ†ตํ•ด์„œ ๊ฑฐ๋ฆฌ๋ฅผ ๊ณ„์‚ฐํ•˜๋ฉฐ ๊ฒฐ๊ณผ๋กœ ๋งŒ๋“ค์–ด์ง„ ํŠธ๋ฆฌ ๊ตฌ์กฐ๋ฅผ ๋ฉ”๋ชจ๋ฆฌ์— ์ €์žฅํ•ฉ๋‹ˆ๋‹ค. ๊ฒ€์ƒ‰ํŠธ๋ฆฌ์˜ ์ข…๋ฅ˜, ๊ฑฐ๋ฆฌ ๋ฉ”ํŠธ๋ฆญ(์œ ํด๋ฆฌ๋””์–ธ, ๋งจํ•˜ํŠผ ๋“ฑ..)์€ ์ „๋ถ€ GridSearch๋กœ ์ž๋™ํ™” ์‹œํ‚ฌ ์ˆ˜ ์žˆ์œผ๋ฉฐ ์ด์— ๋Œ€ํ•œ ์„ค์ •์€ config์—์„œ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ํŠธ๋ฆฌ๊ธฐ๋ฐ˜์˜ ๊ฒ€์ƒ‰ ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— SoftmaxClassifier์™€ ๊ฑฐ์˜ ๋น„์Šทํ•œ ์†๋„๋กœ ํ•™์Šต ๋ฐ ์ถ”๋ก ์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ์‚ฌ์šฉ๋ฒ•์€ ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

from kochat.data import Dataset
from kochat.proc import DistanceClassifier
from kochat.model import intent
from kochat.loss import CenterLoss


dataset = Dataset(ood=True)

# ํ”„๋กœ์„ธ์„œ ์ƒ์„ฑ
clf = DistanceClassifier(
    model=intent.CNN(dataset.intent_dict),
    loss=CenterLoss(dataset.intent_dict)
)

# ๋˜๋„๋ก์ด๋ฉด DistanceClassifier๋Š” Margin ๊ธฐ๋ฐ˜์˜ Loss ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•ด์ฃผ์„ธ์š”
# ํ˜„์žฌ๋Š” CenterLoss, COCOLoss, Cosface, GausianMixture ๋“ฑ์˜ 
# ๊ฑฐ๋ฆฌ๊ธฐ๋ฐ˜ Metric Learning ์ „์šฉ Lossํ•จ์ˆ˜๋ฅผ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.


# ๋ชจ๋ธ ํ•™์Šต
clf.fit(dataset.load_intent(emb))

# ๋ชจ๋ธ ์ถ”๋ก  (์ธํ…ํŠธ ๋ถ„๋ฅ˜)
clf.predict(dataset.load_predict("์˜ค๋Š˜ ์„œ์šธ ๋‚ ์”จ ์–ด๋–จ๊นŒ", emb))



4.3.4. FallbackDetector

SoftmaxClassifier์™€ DistanceClassifier ๋ชจ๋‘ Fallback Detection ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„๋˜์–ด์žˆ์Šต๋‹ˆ๋‹ค. Fallback Detection ๊ธฐ๋Šฅ์„ ์ด์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ์•„๋ž˜์™€ ๊ฐ™์ด ๋‘ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

1. OOD ๋ฐ์ดํ„ฐ๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ : ์ง์ ‘ config์˜ Threshold๋ฅผ ๋งž์ถฐ์•ผํ•ฉ๋‹ˆ๋‹ค.
2. OOD ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ : ๋จธ์‹ ๋Ÿฌ๋‹์„ ์ด์šฉํ•˜์—ฌ Threshold๋ฅผ ์ž๋™ ํ•™์Šตํ•ฉ๋‹ˆ๋‹ค.

๋ฐ”๋กœ ์—ฌ๊ธฐ์—์„œ OOD ๋ฐ์ดํ„ฐ์…‹์ด ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. SoftmaxClassifier๋Š” out distribution ์ƒ˜ํ”Œ๋“ค๊ณผ in distribution ์ƒ˜ํ”Œ๊ฐ„์˜ maximum softmax score (size = [batch_size, 1])๋ฅผ feature๋กœ ํ•˜์—ฌ ๋จธ์‹ ๋Ÿฌ๋‹ ๋ชจ๋ธ์„ ํ•™์Šตํ•˜๊ณ , DistanceClassifier๋Š” out distribution ์ƒ˜ํ”Œ๋“ค๊ณผ in distribution ์ƒ˜ํ”Œ๋“ค์˜ K๊ฐœ์˜ ์ตœ๊ทผ์ ‘ ์ด์›ƒ์˜ ๊ฑฐ๋ฆฌ (size = [batch_size, K])๋ฅผ feature๋กœ ํ•˜์—ฌ ๋จธ์‹ ๋Ÿฌ๋‹ ๋ชจ๋ธ์„ ํ•™์Šตํ•ฉ๋‹ˆ๋‹ค.


์ด๋Ÿฌํ•œ ๋จธ์‹ ๋Ÿฌ๋‹ ๋ชจ๋ธ์„ FallbackDetector๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค. FallbackDetector๋Š” ๊ฐ Classifier์•ˆ์— ๋‚ด์žฅ ๋˜์–ด์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋ณ„๋‹ค๋ฅธ ์ถ”๊ฐ€ ์†Œ์Šค์ฝ”๋“œ ์—†์ด Dataset์˜ ood ํŒŒ๋ผ๋ฏธํ„ฐ๋งŒ True๋กœ ์„ค์ •๋˜์–ด์žˆ๋‹ค๋ฉด Classifierํ•™์Šต์ด ๋๋‚˜๊ณ ๋‚˜์„œ ์ž๋™์œผ๋กœ ํ•™์Šต๋˜๊ณ , predict()์‹œ ์ €์žฅ๋œ FallbackDetector๊ฐ€ ์žˆ๋‹ค๋ฉด ์ž๋™์œผ๋กœ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ FallbackDetector๋กœ ์‚ฌ์šฉํ•  ๋ชจ๋ธ์€ ์•„๋ž˜์ฒ˜๋Ÿผ config์—์„œ ์‚ฌ์šฉ์ž๊ฐ€ ์ง์ ‘ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ GridSearch๋ฅผ ์ง€์›ํ•˜์—ฌ ์—ฌ๋Ÿฌ๊ฐœ์˜ ๋ชจ๋ธ์„ ๋ฆฌ์ŠคํŠธ์— ๋„ฃ์–ด๋‘๋ฉด Kochat ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ€ ํ˜„์žฌ ๋ฐ์ดํ„ฐ์…‹์— ๊ฐ€์žฅ ์ ํ•ฉํ•œ FallbackDetector๋ฅผ ์ž๋™์œผ๋กœ ๊ณจ๋ผ์ค๋‹ˆ๋‹ค.

from sklearn.linear_model import LogisticRegression
from sklearn.svm import LinearSVC


INTENT = {
    # ... (์ƒ๋žต)

    # ํด๋ฐฑ ๋””ํ…ํ„ฐ ํ›„๋ณด (์„ ํ˜• ๋ชจ๋ธ์„ ์ถ”์ฒœํ•ฉ๋‹ˆ๋‹ค)
    'fallback_detectors': [
        LogisticRegression(max_iter=30000),
        LinearSVC(max_iter=30000)

        # ๊ฐ€๋Šฅํ•œ max_iter๋ฅผ ๋†’๊ฒŒ ์„ค์ •ํ•ด์ฃผ์„ธ์š”
        # sklearn default๊ฐ€ max_iter=100์ด๋ผ์„œ ์ˆ˜๋ ด์ด ์•ˆ๋ฉ๋‹ˆ๋‹ค...
    ]
}

Fallback Detection ๋ฌธ์ œ๋Š” Fallback ๋ฉ”ํŠธ๋ฆญ(๊ฑฐ๋ฆฌ or score)๊ฐ€ ์ผ์ • ์ž„๊ณ„์น˜๋ฅผ ๋„˜์–ด๊ฐ€๋ฉด ์ƒ˜ํ”Œ์„ in / out distribution ์ƒ˜ํ”Œ๋กœ ๋ถ„๋ฅ˜ํ•˜๋Š”๋ฐ ๊ทธ ์ž„๊ณ„์น˜๋ฅผ ํ˜„์žฌ ๋ชจ๋ฅด๋Š” ์ƒํ™ฉ์ด๋ฏ€๋กœ ์„ ํ˜• ๋ฌธ์ œ๋กœ ํ•ด์„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ FallbackDetector๋กœ๋Š” ์œ„ ์ฒ˜๋Ÿผ ์„ ํ˜• ๋ชจ๋ธ์ธ ์„ ํ˜• SVM, ๋กœ์ง€์Šคํ‹ฑ ํšŒ๊ท€ ๋“ฑ์„ ์ฃผ๋กœ ์ด์šฉํ•ฉ๋‹ˆ๋‹ค. ๋ฌผ๋ก  ์œ„์˜ ๋ฆฌ์ŠคํŠธ์— RandomForestClassifier()๋‚˜ BernoulliNB(), GradientBoostingClassifier() ๋“ฑ ๋‹ค์–‘ํ•œ sklearn ๋ชจ๋ธ์„ ์ž…๋ ฅํ•ด๋„ ๋™์ž‘์€ ํ•˜์ง€๋งŒ, ์ผ๋ฐ˜์ ์œผ๋กœ ์„ ํ˜•๋ชจ๋ธ์ด ๊ฐ€์žฅ ์šฐ์ˆ˜ํ•˜๊ณ  ์•ˆ์ •์ ์ธ ์„ฑ๋Šฅ์„ ๋ณด์˜€์Šต๋‹ˆ๋‹ค.


์ด๋ ‡๊ฒŒ Fallback์˜ ๋ฉ”ํŠธ๋ฆญ์œผ๋กœ ๋จธ์‹ ๋Ÿฌ๋‹ ๋ชจ๋ธ์„ ํ•™์Šตํ•˜๋ฉด Threshold๋ฅผ ์ง์ ‘ ์œ ์ €๊ฐ€ ์„ค์ •ํ•˜์ง€ ์•Š์•„๋„ ๋ฉ๋‹ˆ๋‹ค. OOD ๋ฐ์ดํ„ฐ์…‹์ด ํ•„์š”ํ•˜๋‹ค๋Š” ์น˜๋ช…์ ์ธ ๋‹จ์ ์ด ์žˆ์ง€๋งŒ, ์ฐจํ›„ ๋ฒ„์ „์—์„œ๋Š” BERT์™€ Markov Chain์„ ์ด์šฉํ•ด OOD ๋ฐ์ดํ„ฐ์…‹์„ ์ž๋™์œผ๋กœ ๋น ๋ฅด๊ฒŒ ์ƒ์„ฑํ•˜๋Š” ๋ชจ๋ธ์„ ๊ตฌํ˜„ํ•˜์—ฌ ์ถ”๊ฐ€ํ•  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค. (์ด ์—…๋ฐ์ดํŠธ ์ดํ›„๋ถ€ํ„ฐ๋Š” OOD ๋ฐ์ดํ„ฐ์…‹์ด ํ•„์š” ์—†์–ด์ง‘๋‹ˆ๋‹ค.)


๊ทธ๋Ÿฌ๋‚˜ ์•„์ง OOD ๋ฐ์ดํ„ฐ์…‹ ์ƒ์„ฑ๊ธฐ๋Šฅ์€ ์ง€์›ํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ํ˜„์žฌ ๋ฒ„์ „์—์„œ๋Š” ๋งŒ์•ฝ OOD ๋ฐ์ดํ„ฐ์…‹์ด ์—†๋‹ค๋ฉด ์‚ฌ์šฉ์ž๊ฐ€ ์ง์ ‘ Threshold๋ฅผ ์„ค์ •ํ•ด์•ผ ํ•˜๋ฏ€๋กœ ๋ˆˆ์œผ๋กœ ์ƒ˜ํ”Œ๋“ค์ด ์–ด๋Š์ •๋„ score ํ˜น์€ ๊ฑฐ๋ฆฌ๋ฅผ ๊ฐ–๋Š”์ง€ ํ™•์ธํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ Kochat์€ Calibrate ๋ชจ๋“œ๋ฅผ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.

while True:
    user_input = dataset.load_predict(input(), emb) 
    # ํ„ฐ๋ฏธ๋„์— ์ง์ ‘ ood๋กœ ์ƒ๊ฐ๋ ๋งŒํ•œ ์ƒ˜ํ”Œ์„ ์ž…๋ ฅํ•ด์„œ 
    # ๋ˆˆ์œผ๋กœ ๊ฒฐ๊ณผ๋ฅผ ์ง์ ‘ ํ™•์ธํ•˜๊ณ , threshold๋ฅผ ์ง์ ‘ ์กฐ์ •ํ•ฉ๋‹ˆ๋‹ค.

    result = clf.predict(user_input, calibrate=True)
    print("classification result : {}".format(result))


# DistanceClassifier
>>> '=====================CALIBRATION_MODE====================='
    'ํ˜„์žฌ ์ž…๋ ฅํ•˜์‹  ๋ฌธ์žฅ๊ณผ ๊ธฐ์กด ๋ฌธ์žฅ๋“ค ์‚ฌ์ด์˜ ๊ฑฐ๋ฆฌ ํ‰๊ท ์€ 2.912์ด๊ณ '
    '๊ฐ€๊นŒ์šด ์ƒ˜ํ”Œ๋“ค๊ณผ์˜ ๊ฑฐ๋ฆฌ๋Š” [2.341, 2.351, 2.412, 2.445 ...]์ž…๋‹ˆ๋‹ค.'
    '์ด ์ˆ˜์น˜๋ฅผ ๋ณด๊ณ  Config์˜ fallback_detection_threshold๋ฅผ ๋งž์ถ”์„ธ์š”.'
    'criteria๋Š” ๊ฑฐ๋ฆฌํ‰๊ท (mean) / ์ตœ์†Ÿ๊ฐ’(min)์œผ๋กœ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.'


# SoftmaxClassifier
>>> '=====================CALIBRATION_MODE====================='
    'ํ˜„์žฌ ์ž…๋ ฅํ•˜์‹  ๋ฌธ์žฅ์˜ softmax logits์€ 0.997์ž…๋‹ˆ๋‹ค.'
    '์ด ์ˆ˜์น˜๋ฅผ ๋ณด๊ณ  Config์˜ fallback_detection_threshold๋ฅผ ๋งž์ถ”์„ธ์š”.'

์ด๋ ‡๊ฒŒ calibrate ๋ชจ๋“œ๋ฅผ ์—ฌ๋Ÿฌ๋ฒˆ ์ง„ํ–‰ํ•˜์…”์„œ ์Šค์Šค๋กœ ๊ณ„์‚ฐํ•œ threshold์™€ ์›ํ•˜๋Š” criteria๋ฅผ ์•„๋ž˜์ฒ˜๋Ÿผ config์— ์„ค์ •ํ•˜๋ฉด ood ๋ฐ์ดํ„ฐ์…‹ ์—†์ด๋„ FallbackDetector๋ฅผ ์ด์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

INTENT = {
    'distance_fallback_detection_criteria': 'mean', # or 'min'  
    # [auto, min, mean], auto๋Š” OOD ๋ฐ์ดํ„ฐ ์žˆ์„๋•Œ๋งŒ ๊ฐ€๋Šฅ
    'distance_fallback_detection_threshold': 3.2,  
    # mean ํ˜น์€ min ์„ ํƒ์‹œ ์ž„๊ณ„๊ฐ’
    
    'softmax_fallback_detection_criteria': 'other',  
    # [auto, other], auto๋Š” OOD ๋ฐ์ดํ„ฐ ์žˆ์„๋•Œ๋งŒ ๊ฐ€๋Šฅ
    'softmax_fallback_detection_threshold': 0.88,  
    # other ์„ ํƒ์‹œ fallback์ด ๋˜์ง€ ์•Š๋Š” ์ตœ์†Œ ๊ฐ’
}

๊ทธ๋Ÿฌ๋‚˜ ์ง€๊ธˆ ๋ฒ„์ „์—์„œ๋Š” ๊ฐ€๊ธ‰์  OOD ๋ฐ์ดํ„ฐ์…‹์„ ์ถ”๊ฐ€ํ•ด์„œ ์ด์šฉํ•ด์ฃผ์„ธ์š”. ์ • ์—†์œผ์‹œ๋ฉด ์ œ๊ฐ€ ๋ฐ๋ชจ ํด๋”์— ๋„ฃ์–ด๋†“์€ ๋ฐ์ดํ„ฐ๋ผ๋„ ๋„ฃ์–ด์„œ ์ž๋™ํ™”ํ•ด์„œ ์“ฐ๋Š”๊ฒŒ ํ›จ์”ฌ ์„ฑ๋Šฅ์ด ์ข‹์Šต๋‹ˆ๋‹ค. ๋ช‡๋ช‡ ๋นŒ๋”๋“ค์€ ์ด ์ž„๊ณ„์น˜๋ฅผ ์ง์ ‘ ์ •ํ•˜๊ฒŒ ํ•˜๊ฑฐ๋‚˜ ๊ทธ๋ƒฅ ์ƒ์ˆ˜๋กœ fixํ•ด๋†“๋Š”๋ฐ, ๊ฐœ์ธ์ ์œผ๋กœ ์ด๊ฑธ ๊ทธ๋ƒฅ ์ƒ์ˆ˜๋กœ fix ํ•ด๋†“๊ฑฐ๋‚˜ ์œ ์ €๋ณด๊ณ  ์ง์ ‘ ์ •ํ•˜๊ฒŒ ํ•˜๋Š”๊ฑด ์ฑ—๋ด‡ ๋นŒ๋”๋กœ์„œ, ํ˜น์€ ํ”„๋ ˆ์ž„์›Œํฌ๋กœ์„œ ๋ฌด์ฑ…์ž„ํ•œ ๊ฒƒ ์•„๋‹Œ๊ฐ€ ์‹ถ์Šต๋‹ˆ๋‹ค.


4.3.5. from kochat.proc import EntityRecongnizer

EntityRecongnizer๋Š” ์—”ํ‹ฐํ‹ฐ ๊ฒ€์ถœ์„ ๋‹ด๋‹นํ•˜๋Š” Entity ๋ชจ๋ธ๋“ค์„ ํ•™์Šต/ํ…Œ์ŠคํŠธ ์‹œํ‚ค๊ณ  ์ถ”๋ก ํ•˜๋Š” ํด๋ž˜์Šค์ž…๋‹ˆ๋‹ค. Entity ๊ฒ€์‚ฌ์˜ ๊ฒฝ์šฐ ๋ฌธ์žฅ 1๊ฐœ๋‹น ๋ผ๋ฒจ์ด ์—ฌ๋Ÿฌ๊ฐœ(๋‹จ์–ด ๊ฐฏ์ˆ˜์™€ ๋™์ผ)์ž…๋‹ˆ๋‹ค. ๋ฌธ์ œ๋Š” Outside ํ† ํฐ์ธ 'O'๊ฐ€ ๋Œ€๋ถ€๋ถ„์ด๊ธฐ ๋•Œ๋ฌธ์— ์ „๋ถ€๋‹ค 'O'๋ผ๊ณ ๋งŒ ์˜ˆ์ธกํ•ด๋„ ๊ฑฐ์˜ 90% ์œก๋ฐ•ํ•˜๋Š” ์ •ํ™•๋„๊ฐ€ ๋‚˜์˜ค๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ, ํŒจ๋“œ์‹œํ€€์‹ฑํ•œ ๋ถ€๋ถ„๋„ 'O'๋กœ ์ฒ˜๋ฆฌ ๋˜์–ด์žˆ๋Š”๋ฐ, ์ด ๋ถ€๋ถ„๋„ ๋งž์€๊ฒƒ์œผ๋กœ ์ƒ๊ฐํ•˜๊ณ  Loss๋ฅผ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค.


์ด๋Ÿฌํ•œ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด Kochat์€ F1 Score, Recall, Precision ๋“ฑ NER์˜ ์„ฑ๋Šฅ์„ ๋ณด๋‹ค ์ •ํ™•ํ•˜๊ฒŒ ํ‰๊ฐ€ ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฐ•๋ ฅํ•œ Validation ๋ฐ ์‹œ๊ฐํ™” ์ง€์›๊ณผ Loss ํ•จ์ˆ˜ ๊ณ„์‚ฐ์‹œ PAD๋ถ€๋ถ„์— masking์„ ์ ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. (mask ์ ์šฉ ์—ฌ๋ถ€ ์—ญ์‹œ config์—์„œ ์„ค์ • ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.) ์‚ฌ์šฉ๋ฒ•์€ ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

from kochat.data import Dataset
from kochat.proc import EntityRecognizer
from kochat.model import entity
from kochat.loss import CRFLoss


dataset = Dataset(ood=True)

# ํ”„๋กœ์„ธ์„œ ์ƒ์„ฑ
rcn = EntityRecognizer(
    model=entity.LSTM(dataset.intent_dict),
    loss=CRFLoss(dataset.intent_dict)
    # Conditional Random Field๋ฅผ Lossํ•จ์ˆ˜๋กœ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.
)


# ๋ชจ๋ธ ํ•™์Šต
rcn.fit(dataset.load_entity(emb))

# ๋ชจ๋ธ ์ถ”๋ก  (์—”ํ‹ฐํ‹ฐ ๊ฒ€์ถœ)
rcn.predict(dataset.load_predict("์˜ค๋Š˜ ์„œ์šธ ๋‚ ์”จ ์–ด๋–จ๊นŒ", emb))



4.4. from kochat.loss

loss ํŒจํ‚ค์ง€๋Š” ์‚ฌ์ „ ์ •์˜๋œ ๋‹ค์–‘ํ•œ built-in Loss ํ•จ์ˆ˜๋“ค์ด ์ €์žฅ๋œ ํŒจํ‚ค์ง€์ž…๋‹ˆ๋‹ค. ํ˜„์žฌ ๋ฒ„์ „์—์„œ๋Š” ์•„๋ž˜ ๋ชฉ๋ก์— ํ•ด๋‹นํ•˜๋Š” Loss ํ•จ์ˆ˜๋“ค์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ์ถ”ํ›„ ๋ฒ„์ „์ด ์—…๋ฐ์ดํŠธ ๋˜๋ฉด ์ง€๊ธˆ๋ณด๋‹ค ํ›จ์”ฌ ๋‹ค์–‘ํ•œ built-in Loss ํ•จ์ˆ˜๋ฅผ ์ง€์›ํ•  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค. ์•„๋ž˜ ๋ชฉ๋ก์„ ์ฐธ๊ณ ํ•˜์—ฌ ์‚ฌ์šฉํ•ด์ฃผ์‹œ๊ธธ ๋ฐ”๋ž๋‹ˆ๋‹ค.


4.4.1. intent loss ํ•จ์ˆ˜

Intent Loss ํ•จ์ˆ˜๋Š” ๊ธฐ๋ณธ์ ์ธ CrossEntropyLoss์™€ ๋‹ค์–‘ํ•œ Distance ๊ธฐ๋ฐ˜์˜ Lossํ•จ์ˆ˜๋ฅผ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. CrossEntropy๋Š” ํ›„์ˆ ํ•  Softmax ๊ธฐ๋ฐ˜์˜ IntentClassifier์— ์ฃผ๋กœ ํ™œ์šฉํ•˜๊ณ , Distance ๊ธฐ๋ฐ˜์˜ Loss ํ•จ์ˆ˜๋“ค์€ Distance ๊ธฐ๋ฐ˜์˜ IntentClassifier์— ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Distance ๊ธฐ๋ฐ˜์˜ Lossํ•จ์ˆ˜๋“ค์€ ์ปดํ“จํ„ฐ ๋น„์ „ ์˜์—ญ (์ฃผ๋กœ ์–ผ๊ตด์ธ์‹) ๋ถ„์•ผ์—์„œ ์ œ์•ˆ๋œ ํ•จ์ˆ˜๋“ค์ด์ง€๋งŒ Intent ๋ถ„๋ฅ˜์˜ Fallback ๋””ํ…์…˜์—๋„ ๋งค์šฐ ์šฐ์ˆ˜ํ•œ ์„ฑ๋Šฅ์„ ๋ณด์ž…๋‹ˆ๋‹ค.


from kochat.loss import CrossEntropyLoss
from kochat.loss import CenterLoss
from kochat.loss import GaussianMixture
from kochat.loss import COCOLoss
from kochat.loss import CosFace


# 1. ๊ฐ€์žฅ ๊ธฐ๋ณธ์ ์ธ Cross Entropy Loss ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค.
cross_entropy = CrossEntropyLoss(label_dict=dataset.intent_dict)

# 2. Intra Class ๊ฐ„์˜ ๊ฑฐ๋ฆฌ๋ฅผ ์ขํž ์ˆ˜ ์žˆ๋Š” Center Loss ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค.
center_loss = CenterLoss(label_dict=dataset.intent_dict)

# 3. Intra Class ๊ฐ„์˜ ๊ฑฐ๋ฆฌ๋ฅผ ์ขํž ์ˆ˜ ์žˆ๋Š” Large Margin Gaussian Mixture Loss ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค.
lmgl = GaussianMixture(label_dict=dataset.intent_dict)

# 4. Inter Class ๊ฐ„์˜ Cosine ๋งˆ์ง„์„ ํ‚ค์šธ ์ˆ˜ ์žˆ๋Š” COCO (Congenerous Cosine) Loss ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค.
coco_loss = COCOLoss(label_dict=dataset.intent_dict)

# 5. Inter Class ๊ฐ„์˜ Cosine ๋งˆ์ง„์„ ํ‚ค์šธ ์ˆ˜ ์žˆ๋Š” Cosface (Large Margin Cosine) Lossํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค.
cosface = CosFace(label_dict=dataset.intent_dict)

4.4.2. entity loss ํ•จ์ˆ˜

Entity Loss ํ•จ์ˆ˜๋Š” ๊ธฐ๋ณธ์ ์ธ CrossEntropyLoss์™€ ํ™•๋ฅ ์  ๋ชจ๋ธ์ธ Conditional Random Field (์ดํ•˜ CRF) Loss๋ฅผ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. CRF Loss๋ฅผ ์ ์šฉํ•˜๋ฉด, EntityRecognizer์˜ ์ถœ๋ ฅ ๊ฒฐ๊ณผ๋ฅผ ๋‹ค์‹œํ•œ๋ฒˆ ๊ต์ •ํ•˜๋Š” ํšจ๊ณผ๋ฅผ ๋ณผ ์ˆ˜ ์žˆ์œผ๋ฉฐ CRF Loss๋ฅผ ์ ์šฉํ•˜๋ฉด, ์ถœ๋ ฅ ๋””์ฝ”๋”ฉ์€ Viterbi ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ํ†ตํ•ด ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

from kochat.loss import CrossEntropyLoss
from kochat.loss import CRFLoss


# 1. ๊ฐ€์žฅ ๊ธฐ๋ณธ์ ์ธ cross entropy ๋กœ์Šค ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค.
cross_entropy = CrossEntropyLoss(label_dict=dataset.intent_dict)

# 2. CRF Loss ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค.
center_loss = CRFLoss(label_dict=dataset.intent_dict)

4.4.3. ์ปค์Šคํ…€ loss ํ•จ์ˆ˜

Kochat์€ ์ปค์Šคํ…€ ๋ชจ๋ธ์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. Pytorch๋กœ ์ž‘์„ฑํ•œ ์ปค์Šคํ…€ ๋ชจ๋ธ์„ ์ง์ ‘ ํ•™์Šต์‹œํ‚ค๊ธฐ๊ณ  ์ฑ—๋ด‡ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๋งŒ์•ฝ ์ปค์Šคํ…€ ๋ชจ๋ธ์„ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ์•„๋ž˜์˜ ๋ช‡๊ฐ€์ง€ ๊ทœ์น™์„ ๋ฐ˜๋“œ์‹œ ๋”ฐ๋ผ์•ผํ•ฉ๋‹ˆ๋‹ค.

  1. forward ํ•จ์ˆ˜์—์„œ ํ•ด๋‹น loss๋ฅผ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค.
  2. compute_loss ํ•จ์ˆ˜์—์„œ ๋ผ๋ฒจ๊ณผ ๋น„๊ตํ•˜์—ฌ ์ตœ์ข… loss๊ฐ’์„ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค.

์•„๋ž˜์˜ ๊ตฌํ˜„ ์˜ˆ์ œ๋ฅผ ๋ณด๋ฉด ๋”์šฑ ์‰ฝ๊ฒŒ ์ดํ•ดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@intent
class CosFace(BaseLoss):

    def __init__(self, label_dict: dict):
        super(CosFace, self).__init__()
        self.classes = len(label_dict)
        self.centers = nn.Parameter(torch.randn(self.classes, self.d_loss))

    def forward(self, feat: Tensor, label: Tensor) -> Tensor:
        # 1. forward ํ•จ์ˆ˜์—์„œ ํ˜„์žฌ lossํ•จ์ˆ˜์˜ loss๋ฅผ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค.

        batch_size = feat.shape[0]
        norms = torch.norm(feat, p=2, dim=-1, keepdim=True)
        nfeat = torch.div(feat, norms)

        norms_c = torch.norm(self.centers, p=2, dim=-1, keepdim=True)
        ncenters = torch.div(self.centers, norms_c)
        logits = torch.matmul(nfeat, torch.transpose(ncenters, 0, 1))

        y_onehot = torch.FloatTensor(batch_size, self.classes)
        y_onehot.zero_()
        y_onehot = Variable(y_onehot).cuda()
        y_onehot.scatter_(1, torch.unsqueeze(label, dim=-1), self.cosface_m)
        margin_logits = self.cosface_s * (logits - y_onehot)
        return margin_logits

    def compute_loss(self, label: Tensor, logits: Tensor, feats: Tensor, mask: nn.Module = None) -> Tensor:
        # 2. compute loss์—์„œ ์ตœ์ข… loss๊ฐ’์„ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค.

        mlogits = self(feats, label)
        # ์ž๊ธฐ ์ž์‹ ์˜ forward ํ˜ธ์ถœ
        
        return F.cross_entropy(mlogits, label)
@intent
class CenterLoss(BaseLoss):
    def __init__(self, label_dict: dict):
        super(CenterLoss, self).__init__()
        self.classes = len(label_dict)
        self.centers = nn.Parameter(torch.randn(self.classes, self.d_loss))
        self.center_loss_function = CenterLossFunction.apply

    def forward(self, feat: Tensor, label: Tensor) -> Tensor:
        # 1. forward ํ•จ์ˆ˜์—์„œ ํ˜„์žฌ lossํ•จ์ˆ˜์˜ loss๋ฅผ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค.

        batch_size = feat.size(0)
        feat = feat.view(batch_size, 1, 1, -1).squeeze()

        if feat.size(1) != self.d_loss:
            raise ValueError("Center's dim: {0} should be equal to input feature's dim: {1}"
                             .format(self.d_loss, feat.size(1)))

        return self.center_loss_function(feat, label, self.centers)

    def compute_loss(self, label: Tensor, logits: Tensor, feats: Tensor, mask: nn.Module = None) -> Tensor:
        # 2. compute loss์—์„œ ์ตœ์ข… loss๊ฐ’์„ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค.

        nll_loss = F.cross_entropy(logits, label)
        center_loss = self(feats, label)
        # ์ž๊ธฐ ์ž์‹ ์˜ forward ํ˜ธ์ถœ

        return nll_loss + self.center_factor * center_loss




4.5. from kochat.app

app ํŒจํ‚ค์ง€๋Š” kochat ๋ชจ๋ธ์„ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์œผ๋กœ ๋ฐฐํฌํ•  ์ˆ˜ ์žˆ๊ฒŒ๋” ํ•ด์ฃผ๋Š” RESTful API์ธ KochatApiํด๋ž˜์Šค์™€ API ํ˜ธ์ถœ์— ๊ด€๋ จ๋œ ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๊ฒŒ๋” ํ•˜๋Š” Scenarioํด๋ž˜์Šค๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.


4.5.1 from kochat.app import Scenario

Scenario ํด๋ž˜์Šค๋Š” ์–ด๋–ค intent์—์„œ๋Š” ์–ด๋–ค entity๊ฐ€ ํ•„์š”ํ•˜๊ณ , ์–ด๋–ค api๋ฅผ ํ˜ธ์ถœํ•˜๋Š”์ง€ ์ •์˜ํ•˜๋Š” ์ผ์ข…์˜ ๋ช…์„ธ์„œ์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์‹œ๋‚˜๋ฆฌ์˜ค ์ž‘์„ฑ์‹œ ์•„๋ž˜์™€ ๊ฐ™์€ ๋ช‡๊ฐ€์ง€ ์ฃผ์˜์‚ฌํ•ญ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

  1. intent๋Š” ๋ฐ˜๋“œ์‹œ raw๋ฐ์ดํ„ฐ ํŒŒ์ผ ๋ช…๊ณผ ๋™์ผํ•˜๊ฒŒ ์„ค์ •ํ•˜๊ธฐ
  2. api๋Š” ํ•จ์ˆ˜ ๊ทธ ์ž์ฒด๋ฅผ ๋„ฃ์Šต๋‹ˆ๋‹ค (๋ฐ˜๋“œ์‹œ callable ํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค.)
  3. scenario ๋”•์…”๋„ˆ๋ฆฌ ์ •์˜์‹œ์— KEY๊ฐ’์€ api ํ•จ์ˆ˜์™€ ์ˆœ์„œ/์ฒ ์ž๊ฐ€ ๋™์ผํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค.
  4. scenario ๋”•์…”๋„ˆ๋ฆฌ ์ •์˜์‹œ์— KEY๊ฐ’์€ config์˜ NER_categories์— ์ •์˜๋œ ์—”ํ‹ฐํ‹ฐ๋งŒ ํ—ˆ์šฉ๋ฉ๋‹ˆ๋‹ค.
  5. ๊ธฐ๋ณธ๊ฐ’(default) ์„ค์ •์„ ์›ํ•˜๋ฉด scenario ๋”•์…”๋„ˆ๋ฆฌ์˜ ๋ฆฌ์ŠคํŠธ์— ๊ฐ’์„ ์ฒจ๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

  • kocrawl (๋‚ ์”จ) ์˜ˆ์ œ
from kochat.app import Scenario
from kocrawl.weather import WeatherCrawler

# kocrawl์€ kochat์„ ๋งŒ๋“ค๋ฉด์„œ ํ•จ๊ป˜ ๊ฐœ๋ฐœํ•œ ํฌ๋กค๋Ÿฌ์ž…๋‹ˆ๋‹ค.
# (https://github.com/gusdnd852/kocrawl)
# 'pip install kocrawl'๋กœ ์†์‰ฝ๊ฒŒ ์„ค์น˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


weather_scenario = Scenario(
    intent='weather',  # intent๋Š” ์ธํ…ํŠธ ๋ช…์„ ์ ์Šต๋‹ˆ๋‹ค (raw ๋ฐ์ดํ„ฐ ํŒŒ์ผ๋ช…๊ณผ ๋™์ผํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค)
    api=WeatherCrawler().request, # API๋Š” ํ•จ์ˆ˜ ์ด๋ฆ„ ์ž์ฒด๋ฅผ ๋„ฃ์Šต๋‹ˆ๋‹ค. (callableํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค)

    scenario={
        'LOCATION': [],
        # ๊ธฐ๋ณธ์ ์œผ๋กœ 'KEY' : []์˜ ํ˜•ํƒœ๋กœ ๋งŒ๋“ญ๋‹ˆ๋‹ค.

        'DATE': ['์˜ค๋Š˜']        
        # entity๊ฐ€ ๊ฒ€์ถœ๋˜์ง€ ์•Š์•˜์„ ๋•Œ default ๊ฐ’์„ ์ง€์ •ํ•˜๊ณ  ์‹ถ์œผ๋ฉด ๋ฆฌ์ŠคํŠธ ์•ˆ์— ์›ํ•˜๋Š” ๊ฐ’์„ ๋„ฃ์Šต๋‹ˆ๋‹ค.
        # [์ „์ฃผ, ๋‚ ์”จ, ์•Œ๋ ค์ค˜] => [S-LOCATION, O, O] => api('์˜ค๋Š˜', S-LOCATION) call
        # ๋งŒ์•ฝ ['์˜ค๋Š˜', 'ํ˜„์žฌ']์ฒ˜๋Ÿผ 2๊ฐœ ์ด์ƒ์˜ default๋ฅผ ๋„ฃ์œผ๋ฉด ๋žœ๋ค์œผ๋กœ ์„ ํƒํ•ด์„œ default ๊ฐ’์œผ๋กœ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.
    }

    # ์‹œ๋‚˜๋ฆฌ์˜ค ๋”•์…”๋„ˆ๋ฆฌ๋ฅผ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.
    # ์ฃผ์˜์  1 : scenario ํ‚ค๊ฐ’(LOCATION, DATE)์˜ ์ˆœ์„œ๋Š” API ํ•จ์ˆ˜์˜ ํŒŒ๋ผ๋ฏธํ„ฐ ์ˆœ์„œ์™€ ๋™์ผํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค.
    # ์ฃผ์˜์  2 : scenario ํ‚ค๊ฐ’(LOCATION, DATE)์˜ ์ฒ ์ž๋Š” API ํ•จ์ˆ˜์˜ ํŒŒ๋ผ๋ฏธํ„ฐ ์ฒ ์ž์™€ ๋™์ผํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค.
    # ์ฃผ์˜์  3 : raw ๋ฐ์ดํ„ฐ ํŒŒ์ผ์— ๋ผ๋ฒจ๋งํ•œ ์—”ํ‹ฐํ‹ฐ๋ช…๊ณผ scenario ํ‚ค๊ฐ’์€ ๋™์ผํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค. 
    #           ์ฆ‰ config์˜ NER_categories์— ๋ฏธ๋ฆฌ ์ •์˜๋œ ์—”ํ‹ฐํ‹ฐ๋งŒ ์‚ฌ์šฉํ•˜์…”์•ผํ•ฉ๋‹ˆ๋‹ค.
    #           B-, I- ๋“ฑ์˜ BIOํƒœ๊ทธ๋Š” ์ƒ๋žตํ•ฉ๋‹ˆ๋‹ค. (S-DATE โ†’ DATE๋กœ ์ƒ๊ฐ)

    # ๋Œ€/์†Œ๋ฌธ์ž๊นŒ์ง€ ๋™์ผํ•  ํ•„์š”๋Š” ์—†๊ณ , ์ฒ ์ž๋งŒ ๊ฐ™์œผ๋ฉด ๋ฉ๋‹ˆ๋‹ค. (๋ชจ๋‘ lowercase ์ƒํƒœ์—์„œ ๋น„๊ต)
    # ๋‹ค์†Œ ๊ท€์ฐฎ๋”๋ผ๋„ ์ •ํ™•ํ•œ ๊ฐ’ ์ „๋‹ฌ์„ ์œ„ํ•ด ์ผ๋ถ€๋Ÿฌ ๋งŒ๋“  ์„ธ ๊ฐ€์ง€ ์ œํ•œ์‚ฌํ•ญ์ด๋‹ˆ ๋”ฐ๋ผ์ฃผ์‹œ๊ธธ ๋ฐ”๋ž๋‹ˆ๋‹ค.

    # WeatherCrawler().request์˜ ํŒŒ๋ผ๋ฏธํ„ฐ๋Š” WeatherCrawler().request(location, date)์ž…๋‹ˆ๋‹ค.
    # APIํŒŒ๋ผ๋ฏธํ„ฐ์™€ ์ˆœ์„œ/์ด๋ฆ„์ด ๋™์ผํ•˜๋ฉฐ, ๋ฐ๋ชจ ๋ฐ์ดํ„ฐ ํŒŒ์ผ์— ์žˆ๋Š” ์—”ํ‹ฐํ‹ฐ์ธ LOCATION, DATE์™€ ๋™์ผํ•ฉ๋‹ˆ๋‹ค.
    # ๋งŒ์•ฝ ํ‹€๋ฆฌ๋ฉด ์–ด๋””์„œ ํ‹€๋ ธ๋Š”์ง€ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€๋กœ ์•Œ๋ ค๋“œ๋ฆฝ๋‹ˆ๋‹ค.
)      

  • ๋ ˆ์Šคํ† ๋ž‘ ์˜ˆ์•ฝ ์‹œ๋‚˜๋ฆฌ์˜ค
from kochat.app import Scenario


reservation_scenario = Scenario(
    intent='reservation',
    api=reservation_check, 
    # reservation_check(num_people, reservation_time)์™€ ๊ฐ™์€
    # ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜์ง€ ๋ง๊ณ  ๊ทธ ์ž์ฒด๋ฅผ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ž…๋ ฅํ•ฉ๋‹ˆ๋‹ค.
    # ํ•จ์ˆ˜๋ฅผ ๋ฐ›์•„์„œ ์ €์žฅํ•ด๋’€๋‹ค๊ฐ€ ์š”์ฒญ ๋ฐœ์ƒ์‹œ Api ๋‚ด๋ถ€์—์„œ call ํ•ฉ๋‹ˆ๋‹ค
    
    scenario={
        'NUM_PEOPLE': [4],
        # NUM_PEOPLE์˜ default๋ฅผ 4๋ช…์œผ๋กœ ์„ค์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.

        'RESERVATION_TIME': []

        # API(reservation_check(num_people, reservation_time)์˜ ํŒŒ๋ผ๋ฏธํ„ฐ์™€ ์ˆœ์„œ/์ฒ ์ž๊ฐ€ ์ผ์น˜ํ•ฉ๋‹ˆ๋‹ค.
        # ์ด ๋•Œ, ๋ฐ˜๋“œ์‹œ NER_categories์— NUM_PEOPLE๊ณผ RESERVATION_TIME์ด ์ •์˜๋˜์–ด ์žˆ์–ด์•ผํ•˜๋ฉฐ,
        # ์‹ค์ œ raw๋ฐ์ดํ„ฐ์— ๋ผ๋ฒจ๋ง๋œ ๋ ˆ์ด๋ธ”๋„ ์œ„์˜ ์ด๋ฆ„์„ ์‚ฌ์šฉํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค.
    }
)     



4.5.2. from kochat.app import KochatApi

KochatApi๋Š” Flask๋กœ ๊ตฌํ˜„๋˜์—ˆ์œผ๋ฉฐ restful api๋ฅผ ์ œ๊ณตํ•˜๋Š” ํด๋ž˜์Šค์ž…๋‹ˆ๋‹ค. ์‚ฌ์‹ค ์„œ๋ฒ„๋กœ ๊ตฌ๋™ํ•  ๊ณ„ํš์ด๋ผ๋ฉด ์œ„์—์„œ ์„ค๋ช…ํ•œ ๊ฒƒ ๋ณด๋‹ค ํ›จ์”ฌ ์‰ฝ๊ฒŒ ํ•™์Šตํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. (ํ•™์Šต์˜ ๋งŽ์€ ๋ถ€๋ถ„๋“ค์ด KochatApi์—์„œ ์ž๋™ํ™” ๋˜๊ธฐ ๋•Œ๋ฌธ์— ํŒŒ๋ผ๋ฏธํ„ฐ ์ „๋‹ฌ๋งŒ์œผ๋กœ ํ•™์Šต์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.) KochatApi ํด๋ž˜์Šค๋Š” ์•„๋ž˜์™€ ๊ฐ™์€ ๋ฉ”์†Œ๋“œ๋“ค์„ ์ง€์›ํ•˜๋ฉฐ ์‚ฌ์šฉ๋ฒ•์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

from kochat.app import KochatApi


# kochat api ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
kochat = KochatApi(
    dataset=dataset, # ๋ฐ์ดํ„ฐ์…‹ ๊ฐ์ฒด
    embed_processor=(emb, True), # ์ž„๋ฒ ๋”ฉ ํ”„๋กœ์„ธ์„œ, ํ•™์Šต์—ฌ๋ถ€
    intent_classifier=(clf, True), # ์ธํ…ํŠธ ๋ถ„๋ฅ˜๊ธฐ, ํ•™์Šต์—ฌ๋ถ€
    entity_recognizer=(rcn, True), # ์—”ํ‹ฐํ‹ฐ ๊ฒ€์ถœ๊ธฐ, ํ•™์Šต์—ฌ๋ถ€
    scenarios=[ #์‹œ๋‚˜๋ฆฌ์˜ค ๋ฆฌ์ŠคํŠธ
        weather, dust, travel, restaurant
    ]
)

# kochat.app์€ FLask ๊ฐ์ฒด์ž…๋‹ˆ๋‹ค. 
# Flask์˜ ์‚ฌ์šฉ๋ฒ•๊ณผ ๋™์ผํ•˜๊ฒŒ ์‚ฌ์šฉํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.
@kochat.app.route('/')
def index():
    return render_template("index.html")


# ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์„œ๋ฒ„๋ฅผ ๊ฐ€๋™ํ•ฉ๋‹ˆ๋‹ค.
if __name__ == '__main__':
    kochat.app.template_folder = kochat.root_dir + 'templates'
    kochat.app.static_folder = kochat.root_dir + 'static'
    kochat.app.run(port=8080, host='0.0.0.0')

์œ„์™€ ๊ฐ™์ด kochat ์„œ๋ฒ„๋ฅผ ์‹คํ–‰์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. (์›ฌ๋งŒํ•˜๋ฉด ์œ„์™€ ๊ฐ™์ด template๊ณผ static์„ ๋ช…์‹œ์ ์œผ๋กœ ์ ์–ด์ฃผ์„ธ์š”.) ์œ„ ์˜ˆ์‹œ์ฒ˜๋Ÿผ ๋ทฐ๋ฅผ ์ง์ ‘ ์„œ๋ฒ„์— ์—ฐ๊ฒฐํ•ด์„œ ํ•˜๋‚˜์˜ ์„œ๋ฒ„์—์„œ ๋ทฐ์™€ ๋”ฅ๋Ÿฌ๋‹ ์ฝ”๋“œ๋ฅผ ๋ชจ๋‘ ๊ตฌ๋™์‹œํ‚ฌ ์ˆ˜๋„ ์žˆ๊ณ , ๋งŒ์•ฝ Micro Service Architecture๋ฅผ ๊ตฌ์„ฑํ•ด์•ผํ•œ๋‹ค๋ฉด, ์ฑ—๋ด‡ ์„œ๋ฒ„์˜ index route ('/')๋“ฑ์„ ์„ค์ •ํ•˜์ง€ ์•Š๊ณ  ๋”ฅ๋Ÿฌ๋‹ ๋ฐฑ์—”๋“œ ์„œ๋ฒ„๋กœ๋„ ์ถฉ๋ถ„ํžˆ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋งŒ์•ฝ ํ•™์Šต์„ ์›ํ•˜์ง€ ์•Š์„ ๋•Œ๋Š” ์•„๋ž˜์ฒ˜๋Ÿผ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค.

# 1. Tuple์˜ ๋‘๋ฒˆ์งธ ์ธ์ž์— False ์ž…๋ ฅ
kochat = KochatApi(
    dataset=dataset, # ๋ฐ์ดํ„ฐ์…‹ ๊ฐ์ฒด
    embed_processor=(emb, False), # ์ž„๋ฒ ๋”ฉ ํ”„๋กœ์„ธ์„œ, ํ•™์Šต์—ฌ๋ถ€
    intent_classifier=(clf, False), # ์ธํ…ํŠธ ๋ถ„๋ฅ˜๊ธฐ, ํ•™์Šต์—ฌ๋ถ€
    entity_recognizer=(rcn, False), # ์—”ํ‹ฐํ‹ฐ ๊ฒ€์ถœ๊ธฐ, ํ•™์Šต์—ฌ๋ถ€
    scenarios=[ #์‹œ๋‚˜๋ฆฌ์˜ค ๋ฆฌ์ŠคํŠธ
        weather, dust, travel, restaurant
    ]
)

# 2. Tuple์— ํ”„๋กœ์„ธ์„œ๋งŒ ์ž…๋ ฅ
kochat = KochatApi(
    dataset=dataset, # ๋ฐ์ดํ„ฐ์…‹ ๊ฐ์ฒด
    embed_processor=(emb), # ์ž„๋ฒ ๋”ฉ ํ”„๋กœ์„ธ์„œ
    intent_classifier=(clf), # ์ธํ…ํŠธ ๋ถ„๋ฅ˜๊ธฐ
    entity_recognizer=(rcn), # ์—”ํ‹ฐํ‹ฐ ๊ฒ€์ถœ๊ธฐ
    scenarios=[ #์‹œ๋‚˜๋ฆฌ์˜ค ๋ฆฌ์ŠคํŠธ
        weather, dust, travel, restaurant
    ]
)

# 3. ๊ทธ๋ƒฅ ํ”„๋กœ์„ธ์„œ๋งŒ ์ž…๋ ฅ
kochat = KochatApi(
    dataset=dataset, # ๋ฐ์ดํ„ฐ์…‹ ๊ฐ์ฒด
    embed_processor=emb, # ์ž„๋ฒ ๋”ฉ ํ”„๋กœ์„ธ์„œ
    intent_classifier=clf, # ์ธํ…ํŠธ ๋ถ„๋ฅ˜๊ธฐ
    entity_recognizer=rcn, # ์—”ํ‹ฐํ‹ฐ ๊ฒ€์ถœ๊ธฐ
    scenarios=[ #์‹œ๋‚˜๋ฆฌ์˜ค ๋ฆฌ์ŠคํŠธ
        weather, dust, travel, restaurant
    ]
)

์•„๋ž˜์—์„œ๋Š” Kochat ์„œ๋ฒ„์˜ url ํŒจํ„ด์— ๋Œ€ํ•ด ์ž์„ธํ•˜๊ฒŒ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค. ํ˜„์žฌ kochat api๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ 4๊ฐœ์˜ url ํŒจํ„ด์„ ์ง€์›ํ•˜๋ฉฐ, ์ด url ํŒจํ„ด๋“ค์€ config์˜ API ์ฑ•ํ„ฐ์—์„œ ๋ณ€๊ฒฝ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

API = {
    'request_chat_url_pattern': 'request_chat',  # request_chat ๊ธฐ๋Šฅ url pattern
    'fill_slot_url_pattern': 'fill_slot',  # fill_slot ๊ธฐ๋Šฅ url pattern
    'get_intent_url_pattern': 'get_intent',  # get_intent ๊ธฐ๋Šฅ url pattern
    'get_entity_url_pattern': 'get_entity'  # get_entity ๊ธฐ๋Šฅ url pattern
}

4.5.2.1. request_chat

๊ฐ€์žฅ ๊ธฐ๋ณธ์ ์ธ ํŒจํ„ด์ธ request_chat์ž…๋‹ˆ๋‹ค. intent๋ถ„๋ฅ˜, entity๊ฒ€์ถœ, api์—ฐ๊ฒฐ์„ ํ•œ๋ฒˆ์— ์ง„ํ–‰ํ•ฉ๋‹ˆ๋‹ค.
๊ธฐ๋ณธ ํŒจํ„ด : https://0.0.0.0/request_chat//

case 1. state SUCCESS
๋ชจ๋“  entity๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ์ž…๋ ฅ๋œ ๊ฒฝ์šฐ state 'SUCCESS'๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

>>> ์œ ์ € gusdnd852 : ๋ชจ๋ ˆ ๋ถ€์‚ฐ ๋‚ ์”จ ์–ด๋•Œ

https://123.456.789.000:1234/request_chat/gusdnd852/๋ชจ๋ ˆ ๋ถ€์‚ฐ ๋‚ ์”จ ์–ด๋•Œ
โ†’ {
    'input': [๋ชจ๋ ˆ, ๋ถ€์‚ฐ, ๋‚ ์”จ, ์–ด๋•Œ],
    'intent': 'weather',
    'entity': [S-DATE, S-LOCATION, O, O]
    'state': 'SUCCESS',
    'answer': '๋ถ€์‚ฐ์˜ ๋‚ ์”จ ์ •๋ณด๋ฅผ ์ „ํ•ด๋“œ๋ฆด๊ฒŒ์š”. ๐Ÿ˜‰
               ๋ชจ๋ ˆ ๋ถ€์‚ฐ์ง€์—ญ์€ ์˜ค์ „์—๋Š” ์„ญ์”จ 19๋„์ด๋ฉฐ, ์•„๋งˆ ํ•˜๋Š˜์ด ๋ง‘์„ ๊ฒƒ ๊ฐ™์•„์š”. ์˜คํ›„์—๋„ ์„ญ์”จ 26๋„์ด๋ฉฐ, ์•„๋งˆ ํ•˜๋Š˜์ด ๋ง‘์„ ๊ฒƒ ๊ฐ™์•„์š”.'
}


case 2. state REQUIRE_XXX
๋งŒ์•ฝ default๊ฐ’์ด ์—†๋Š” ์—”ํ‹ฐํ‹ฐ๊ฐ€ ์ž…๋ ฅ๋˜์ง€ ์•Š์€ ๊ฒฝ์šฐ state 'REQUIRE_XXX'๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
๋‘๊ฐœ ์ด์ƒ์˜ ์—”ํ‹ฐํ‹ฐ๊ฐ€ ๋ชจ์ž๋ผ๋ฉด state 'REQUIRE_XXX_YYY'๊ฐ€ ๋ฐ˜ํ™˜๋ฉ๋‹ˆ๋‹ค.

>>> ์œ ์ € minqukanq : ๋ชฉ์š”์ผ ๋‚ ์”จ ์–ด๋•Œ

e.g. https://123.456.789.000:1234/request_chat/minqukanq/๋ชฉ์š”์ผ ๋‚ ์”จ ์–ด๋•Œ
โ†’ {
    'input': [๋ชฉ์š”์ผ, ๋‚ ์”จ, ์–ด๋•Œ],
    'intent': 'weather',
    'entity': [S-DATE, O, O]
    'state': 'REQUIRE_LOCATION',
    'answer': None
}


case 3. state FALLBACK
์ธํ…ํŠธ ๋ถ„๋ฅ˜์‹œ FALLBACK์ด ๋ฐœ์ƒํ•˜๋ฉด FALLBACK์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

>>> ์œ ์ € sangji11 : ๋ชฉ์š”์ผ ์นœ๊ตฌ ์ƒ์ผ์ด๋‹ค

e.g. https://123.456.789.000:1234/request_chat/sangji11/๋ชฉ์š”์ผ ์นœ๊ตฌ ์ƒ์ผ์ด๋‹ค
โ†’ {
    'input': [๋ชฉ์š”์ผ, ์นœ๊ตฌ, ์ƒ์ผ์ด๋‹ค],
    'intent': 'FALLBACK',
    'entity': [S-DATE, O, O]
    'state': 'FALLBACK',
    'answer': None
}

4.5.2.2. fill_slot

๊ฐ€์žฅ request์‹œ REQUIRE_XXX๊ฐ€ ๋‚˜์˜ฌ๋•Œ, ์‚ฌ์šฉ์ž์—๊ฒŒ ๋˜๋ฌป๊ณ  ๊ธฐ์กด ๋”•์…”๋„ˆ๋ฆฌ์— ์ถ”๊ฐ€ํ•ด์„œ api๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.
๊ธฐ๋ณธ ํŒจํ„ด : https://0.0.0.0/fill_slot//

>>> ์œ ์ € gusdnd852 : ๋ชจ๋ ˆ ๋‚ ์”จ ์•Œ๋ ค์ค˜ โ†’ REQUIRE_LOCATION
>>> ๋ด‡ : ์–ด๋Š ์ง€์—ญ์„ ์•Œ๋ ค๋“œ๋ฆด๊นŒ์š”?
>>> ์œ ์ € gusdnd852 : ๋ถ€์‚ฐ

https://123.456.789.000:1234/fill_slot/gusdnd852/๋ถ€์‚ฐ
โ†’ {
    'input': [๋ถ€์‚ฐ] + [๋ชจ๋ ˆ, ๋‚ ์”จ, ์–ด๋•Œ],
    'intent': 'weather',
    'entity': [S-LOCATION] + [S-DATE, O, O]
    'state': 'SUCCESS',
    'answer': '๋ถ€์‚ฐ์˜ ๋‚ ์”จ ์ •๋ณด๋ฅผ ์ „ํ•ด๋“œ๋ฆด๊ฒŒ์š”. ๐Ÿ˜‰
               ๋ชจ๋ ˆ ๋ถ€์‚ฐ์ง€์—ญ์€ ์˜ค์ „์—๋Š” ์„ญ์”จ 19๋„์ด๋ฉฐ, ์•„๋งˆ ํ•˜๋Š˜์ด ๋ง‘์„ ๊ฒƒ ๊ฐ™์•„์š”. ์˜คํ›„์—๋„ ์„ญ์”จ 26๋„์ด๋ฉฐ, ์•„๋งˆ ํ•˜๋Š˜์ด ๋ง‘์„ ๊ฒƒ ๊ฐ™์•„์š”.'
}


>>> ์œ ์ € gusdnd852 : ๋‚ ์”จ ์•Œ๋ ค์ค˜ โ†’ REQUIRE_DATE_LOCATION
>>> ๋ด‡ : ์–ธ์ œ์˜ ์–ด๋Š ์ง€์—ญ์„ ๋‚ ์”จ๋ฅผ ์•Œ๋ ค๋“œ๋ฆด๊นŒ์š”?
>>> ์œ ์ € gusdnd852 : ๋ถ€์‚ฐ ๋ชจ๋ ˆ

https://123.456.789.000:1234/fill_slot/gusdnd852/๋ถ€์‚ฐ ๋ชจ๋ ˆ
โ†’ {
    'input': [๋ถ€์‚ฐ, ๋ชจ๋ ˆ] + [๋‚ ์”จ, ์–ด๋•Œ],
    'intent': 'weather',
    'entity': [S-LOCATION, S-DATE] + [O, O]
    'state': 'SUCCESS',
    'answer': '๋ถ€์‚ฐ์˜ ๋‚ ์”จ ์ •๋ณด๋ฅผ ์ „ํ•ด๋“œ๋ฆด๊ฒŒ์š”. ๐Ÿ˜‰
               ๋ชจ๋ ˆ ๋ถ€์‚ฐ์ง€์—ญ์€ ์˜ค์ „์—๋Š” ์„ญ์”จ 19๋„์ด๋ฉฐ, ์•„๋งˆ ํ•˜๋Š˜์ด ๋ง‘์„ ๊ฒƒ ๊ฐ™์•„์š”. ์˜คํ›„์—๋„ ์„ญ์”จ 26๋„์ด๋ฉฐ, ์•„๋งˆ ํ•˜๋Š˜์ด ๋ง‘์„ ๊ฒƒ ๊ฐ™์•„์š”.'
}

4.5.2.3. get_intent

intent๋งŒ ์•Œ๊ณ ์‹ถ์„๋•Œ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.
๊ธฐ๋ณธ ํŒจํ„ด : https://0.0.0.0/get_intent/


https://123.456.789.000:1234/get_intent/์ „์ฃผ ๋‚ ์”จ ์–ด๋•Œ
โ†’ {
    'input': [์ „์ฃผ, ๋‚ ์”จ, ์–ด๋•Œ],
    'intent': 'weather',
    'entity': None,
    'state': 'REQUEST_INTENT',
    'answer': None
}

4.5.2.4. get_entity

entity๋งŒ ์•Œ๊ณ ์‹ถ์„๋•Œ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.
๊ธฐ๋ณธ ํŒจํ„ด : https://0.0.0.0/get_entity/


https://123.456.789.000:1234/get_entity/์ „์ฃผ ๋‚ ์”จ ์–ด๋•Œ
โ†’ {
    'input': [์ „์ฃผ, ๋‚ ์”จ, ์–ด๋•Œ],
    'intent': None,
    'entity': [S-LOCATION, O, O],
    'state': 'REQUEST_ENTITY',
    'answer': None
}

5. Visualization Support

Kochat์€ ์•„๋ž˜์™€ ๊ฐ™์ด ๋‹ค์–‘ํ•œ ์‹œ๊ฐํ™” ๊ธฐ๋Šฅ์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. Feature Space๋Š” ์ผ์ • Epoch๋งˆ๋‹ค ๋ฉ”๋ชจ๋ฆฌ์— ์ €์žฅ๋˜๊ณ , ๊ทธ ์™ธ์˜ ์‹œ๊ฐํ™” ์ž๋ฃŒ๋Š” ๋งค Epoch๋งˆ๋‹ค ๊ณ„์† ์—…๋ฐ์ดํŠธ ๋˜๋ฉฐ "root/saved"์— ๋ชจ๋ธ ์ €์žฅํŒŒ์ผ๊ณผ ํ•จ๊ป˜ ์ €์žฅ๋ฉ๋‹ˆ๋‹ค. ์‹œ๊ฐํ™” ์ž๋ฃŒ ๋ฐ ๋ชจ๋ธ ์ €์žฅ ๊ฒฝ๋กœ๋Š” config์—์„œ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

5.1. Train/Test Accuracy



5.2. Train/Test Recall (macro average)



5.3. Train/Test Precision (macro average)



5.4. Train/Test F1-Score (macro average)



5.5. Train/Test Confusion Matrix

Confusion Matrix์˜ ๊ฒฝ์šฐ๋Š” X์ถ•(์•„๋ž˜)๊ฐ€ Prediction, Y์ถ•(์™ผ์ชฝ)์ด Label์ž…๋‹ˆ๋‹ค.
๋‹ค์Œ ๋ฒ„์ „์—์„œ xticks์™€ yticks๋ฅผ ์ถ”๊ฐ€ํ•  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.



5.6. Train/Test Classification Performance Report

Accuracy, Precision, Recall, F1 Score ๋“ฑ ๋ชจ๋ธ์„ ๋‹ค์–‘ํ•œ ๋ฉ”ํŠธ๋ฆญ์œผ๋กœ ํ‰๊ฐ€ํ•˜๊ณ , ํ‘œ ํ˜•ํƒœ๋กœ ์ด๋ฏธ์ง€ํŒŒ์ผ์„ ๋งŒ๋“ค์–ด์ค๋‹ˆ๋‹ค.


์†Œ์ˆ˜์  ๋ช‡๋ฒˆ์งธ ๊นŒ์ง€ ๋ฐ˜์˜ฌ๋ฆผํ•ด์„œ ๋ณด์—ฌ์ค„์ง€ config์—์„œ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

PROC = {
    # ...(์ƒ๋žต)
    'logging_precision': 5,  # ๊ฒฐ๊ณผ ์ €์žฅ์‹œ ๋ฐ˜์˜ฌ๋ฆผ ์†Œ์ˆ˜์  n๋ฒˆ์งธ์—์„œ ๋ฐ˜์˜ฌ๋ฆผ
}



5.7. Train/Test Fallback Detection Performance Report

Fallback Detection์€ Intent Classification์˜ ์˜์—ญ์ž…๋‹ˆ๋‹ค. Intent Classification๋งŒ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. (Fallback Detection ์„ฑ๋Šฅ ํ‰๊ฐ€๋ฅผ ์œ„ํ•ด์„œ๋Š” ๋ฐ˜๋“œ์‹œ ood=True์—ฌ์•ผํ•ฉ๋‹ˆ๋‹ค.)



5.8. Feature Space Visualization

Feature Space๋Š” Distance ๊ธฐ๋ฐ˜์˜ Metric Learning Lossํ•จ์ˆ˜๊ฐ€ ์ž˜ ์ž‘๋™ํ•˜๊ณ  ์žˆ๋Š”์ง€ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•œ๊ฒƒ์œผ๋กœ Intent Classification๋งŒ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ ์‹œ๊ฐํ™” ์ฐจ์›์€ config์˜ d_loss์— ๋”ฐ๋ผ ๊ฒฐ์ •๋ฉ๋‹ˆ๋‹ค.

  • d_loss = 2์ธ ๊ฒฝ์šฐ : 2์ฐจ์›์œผ๋กœ ์‹œ๊ฐํ™”
  • d_loss = 3์ธ ๊ฒฝ์šฐ : 3์ฐจ์›์œผ๋กœ ์‹œ๊ฐํ™”
  • d_loss > 3์ธ ๊ฒฝ์šฐ : Incremetal PCA๋ฅผ ํ†ตํ•ด 3์ฐจ์›์œผ๋กœ ์ฐจ์› ๊ฐ์†Œ ํ›„ ์‹œ๊ฐํ™”



Feature Space Visualization์€ PCA๋ฅผ ์‹คํ–‰ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋น„์šฉ์ด ์ƒ๋‹นํžˆ ํฝ๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ์‹œ๊ฐํ™”๋Š” ๋งค Epoch๋งˆ๋‹ค ์ˆ˜ํ–‰ํ•˜์ง€๋งŒ, Feature Space Visulization์€ ๋ช‡ Epoch๋งˆ๋‹ค ์ˆ˜ํ–‰ํ• ์ง€ ๊ฒฐ์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

PROC = {
    # ...(์ƒ๋žต)
    'visualization_epoch': 50,  # ์‹œ๊ฐํ™” ๋นˆ๋„ (์• ํญ๋งˆ๋‹ค ์‹œ๊ฐํ™” ์ˆ˜ํ–‰)
}



6. Performance Issue

์ด ์ฑ•ํ„ฐ๋Š” Kochat์˜ ๋‹ค์–‘ํ•œ ์„ฑ๋Šฅ ์ด์Šˆ์— ๋Œ€ํ•ด ๊ธฐ๋กํ•ฉ๋‹ˆ๋‹ค.



6.1. ์–ผ๊ตด์ธ์‹ ์˜์—ญ์—์„œ ์“ฐ์ด๋˜ Loss ํ•จ์ˆ˜๋“ค์€ Fallback ๋””ํ…์…˜์— ํšจ๊ณผ์ ์ด๋‹ค.

์‚ฌ์‹ค CenterLoss๋‚˜ CosFace ๊ฐ™์€ Margin Lossํ•จ์ˆ˜๋“ค์ด ์ปดํ“จํ„ฐ ๋น„์ „์˜ ์–ผ๊ตด์ธ์‹ ์˜์—ญ์—์„œ ๋งŽ์ด ์“ฐ์ธ๋‹ค๊ณ ๋Š” ํ•˜๋‚˜ ๊ธฐ๋ณธ์ ์œผ๋กœ ๋ชจ๋“  Retrieval ๋ฌธ์ œ์— ์ ์šฉํ•  ์ˆ˜ ์žˆ๋Š” Lossํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค. Kochat์˜ DistanceClassifier๋Š” ๊ฑฐ๋ฆฌ๊ธฐ๋ฐ˜์˜ Retrieval์„ ์ˆ˜ํ–‰ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ด๋Ÿฌํ•œ Lossํ•จ์ˆ˜๋ฅผ ๋งค์šฐ ํšจ๊ณผ์ ์œผ๋กœ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์‹ค์ œ๋กœ ๋ฐ๋ชจ ๋ฐ์ดํ„ฐ์…‹์— ์ ์šฉํ–ˆ์„ ๋•Œ CrossEntropyLoss๋กœ๋Š” 70% ์–ธ์ €๋ฆฌ์ธ FallbackDetection ์„ฑ๋Šฅ์ด CenterLoss, CosFace ๋“ฑ์„ ์ ์šฉํ•˜๋ฉด 90~95%๊นŒ์ง€ ํ–ฅ์ƒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. (120๊ฐœ์˜ OOD ์ƒ˜ํ”Œ ํ…Œ์ŠคํŠธ)

  • SoftmaxClassifier + CrossEntropyLoss + CNN (d_model=512, layers=1)


  • DistanceClassifier + CrossEntropyLoss + CNN (d_model=512, layers=1)


  • DistanceClassifier + CenterLoss + CNN (d_model=512, layers=1)


6.2. Retrieval Feature๋กœ๋Š” LSTM๋ณด๋‹ค CNN์ด ๋” ์ข‹๋‹ค.

Retrieval ๊ธฐ๋ฐ˜์˜ Distance Classification์˜ ๊ฒฝ์šฐ LSTM๋ณด๋‹ค CNN์˜ Feature๋“ค์ด ํด๋ž˜์Šค๋ณ„๋กœ ํ›จ์”ฌ ์ž˜ ๊ตฌ๋ถ„๋˜๋Š” ๊ฒƒ์„ ํ™•์ธํ–ˆ์Šต๋‹ˆ๋‹ค. Feature Extraction ๋Šฅ๋ ฅ ์ž์ฒด๋Š” CNN์ด ์ข‹๋‹ค๊ณ  ์•Œ๋ ค์ง„ ๊ฒƒ์ฒ˜๋Ÿผ ์•„๋ฌด๋ž˜๋„ CNN์ด Feature๋ฅผ ๋” ์ž˜ ๋ฝ‘์•„๋‚ด๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. Feature Space์—์„œ ๊ตฌ๋ถ„์ด ์ž˜ ๋œ๋‹ค๋Š” ๊ฒƒ์€ OOD ์„ฑ๋Šฅ์ด ์šฐ์ˆ˜ํ•˜๋‹ค๋Š” ๊ฒƒ๊ณผ ๋™์น˜์ด๋ฏ€๋กœ, DistanceClassifier ์‚ฌ์šฉ์‹œ LSTM๋ณด๋‹จ CNN์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ๋”์šฑ ๋ฐ”๋žŒ์งํ•ด๋ณด์ž…๋‹ˆ๋‹ค.

  • ์ขŒ : LSTM (d_model=512, layers=1) + CosFace, 500 Epoch ํ•™์Šต (์ˆ˜๋ ดํ•จ)
  • ์šฐ : CNN (d_model=512, layers=1) + CosFace, 500 Epoch ํ•™์Šต (์ˆ˜๋ ดํ•จ)

image



6.3. CRF Loss์˜ ์ˆ˜๋ ด ์†๋„๋Š” CrossEntropy๋ณด๋‹ค ๋Š๋ฆฌ๋‹ค.

EntityRecognizer์˜ ๊ฒฝ์šฐ ๋™์ผ ์‚ฌ์ด์ฆˆ, ๋™์ผ Layer์—์„œ CRF Loss๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํ™•์‹คํžˆ ์„ฑ๋Šฅ์€ ๋”์šฑ ์šฐ์ˆ˜ํ•ด์ง€๋‚˜, ์กฐ๊ธˆ ๋” ๋” ๋Š๋ฆฌ๊ฒŒ ์ˆ˜๋ ดํ•˜๋Š” ๊ฒƒ์„ ํ™•์ธํ–ˆ์Šต๋‹ˆ๋‹ค. CRF Loss์˜ ๊ฒฝ์šฐ ์กฐ๊ธˆ ๋” ๋งŽ์€ ํ•™์Šต ์‹œ๊ฐ„์„ ์ค˜์•ผ ์ œ ์„ฑ๋Šฅ์„ ๋‚ด๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  • ์ขŒ : LSTM (d_model=512, layers=1) + CrossEntropy โ†’ Epoch 300์— f1-score 90% ๋„๋‹ฌ
  • ์šฐ : LSTM (d_model=512, layers=1) + CRFLoss โ†’ Epoch 450์— f1-score 90% ๋„๋‹ฌ



6.4. FallbackDetector์˜ max_iter๋Š” ๋†’๊ฒŒ ์„ค์ •ํ•ด์•ผํ•œ๋‹ค.

Fallback Detector๋Š” sklearn ๋ชจ๋ธ๋“ค์„ ํ™œ์šฉํ•˜๋Š”๋ฐ ๊ธฐ์กด sklearn๋ชจ๋ธ๋“ค์€ max_iter์˜ default๊ฐ’์ด 100์œผ๋กœ ์„ค์ •๋˜์–ด ์ˆ˜๋ ดํ•˜๊ธฐ ์ „์— ํ•™์Šต์ด ๋๋‚˜๋ฒ„๋ฆฝ๋‹ˆ๋‹ค. ๋•Œ๋ฌธ์— Fallback Detector๋ฅผ config์— ์ •์˜ํ• ๋•Œ max_iter๋ฅผ ๋†’๊ฒŒ ์„ค์ •ํ•ด์•ผ ์ถฉ๋ถ„ํ•œ ํ•™์Šต์‹œ๊ฐ„์„ ๋ณด์žฅ๋ฐ›์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


7. Demo Application

์ด ์ฑ•ํ„ฐ์—์„œ๋Š” Demo ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ๋Œ€ํ•ด ์†Œ๊ฐœํ•ฉ๋‹ˆ๋‹ค. ๋ฐ๋ชจ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ์—ฌํ–‰์ •๋ณด๋ฅผ ์†Œ๊ฐœํ•˜๋Š” ์ฑ—๋ด‡ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์œผ๋กœ, ๋‚ ์”จ, ๋ฏธ์„ธ๋จผ์ง€, ๋ง›์ง‘ ์—ฌํ–‰์ง€ ์ •๋ณด๋ฅผ ์•Œ๋ ค์ฃผ๋Š” ๊ธฐ๋Šฅ์„ ๋ณด์œ ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. Api๋Š” Kochat์„ ๋งŒ๋“ค๋ฉด์„œ ํ•จ๊ป˜ ๋งŒ๋“  Kocrawl ์„ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.

7.1. View (HTML)

Html๊ณผ CSS๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ View๋ฅผ ๊ตฌํ˜„ํ•˜์˜€์Šต๋‹ˆ๋‹ค. ์ œ๊ฐ€ ๋””์ž์ธ ํ•œ ๊ฒƒ์€ ์•„๋‹ˆ๊ณ  ์—ฌ๊ธฐ ์—์„œ ์ œ๊ณต๋˜๋Š” ๋ถ€ํŠธ์ŠคํŠธ๋žฉ ํ…Œ๋งˆ๋ฅผ ์‚ฌ์šฉํ•˜์˜€์Šต๋‹ˆ๋‹ค.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Kochat ๋ฐ๋ชจ</title>

    <script src="{{ url_for('static', filename="js/jquery.js") }}" type="text/javascript"></script>
    <script src="{{ url_for('static', filename="js/bootstrap.js") }}" type="text/javascript"></script>
    <script src="{{ url_for('static', filename="js/main.js") }}" type="text/javascript"></script>

    <link href="{{ url_for('static', filename="css/bootstrap.css") }}" rel="stylesheet" id="bootstrap-css">
    <link href="{{ url_for('static', filename="css/main.css") }}" rel="stylesheet" id="main-css">
    <script>
        greet();
        onClickAsEnter();
    </script>
</head>

<body>
<div class="chat_window">
    <div class="top_menu">
        <div class="buttons">
            <div class="button close_button"></div>
            <div class="button minimize"></div>
            <div class="button maximize"></div>
        </div>
        <div class="title">Kochat ๋ฐ๋ชจ</div>
    </div>
    <ul class="messages"></ul>
    <div class="bottom_wrapper clearfix">
        <div class="message_input_wrapper">
            <input class="message_input"
                   onkeyup="return onClickAsEnter(event)"
                   placeholder="๋‚ด์šฉ์„ ์ž…๋ ฅํ•˜์„ธ์š”."/>
        </div>

        <div class="send_message"
             id="send_message"
             onclick="onSendButtonClicked()">

            <div class="icon"></div>
            <div class="text">๋ณด๋‚ด๊ธฐ</div>
        </div>

    </div>
</div>
<div class="message_template">
    <li class="message">
        <div class="avatar"></div>
        <div class="text_wrapper">
            <div class="text"></div>
        </div>
    </li>
</div>
</body>
</html>



7.2. ๋”ฅ๋Ÿฌ๋‹ ๋ชจ๋ธ ๊ตฌ์„ฑ

์•„๋ž˜์™€ ๊ฐ™์€ ๋ชจ๋ธ ๊ตฌ์„ฑ์„ ์‚ฌ์šฉํ•˜์˜€์Šต๋‹ˆ๋‹ค.

dataset = Dataset(ood=True)
emb = GensimEmbedder(model=embed.FastText())

clf = DistanceClassifier(
    model=intent.CNN(dataset.intent_dict),
    loss=CenterLoss(dataset.intent_dict)
)

rcn = EntityRecognizer(
    model=entity.LSTM(dataset.entity_dict),
    loss=CRFLoss(dataset.entity_dict)
)

kochat = KochatApi(
    dataset=dataset,
    embed_processor=(emb, True),
    intent_classifier=(clf, True),
    entity_recognizer=(rcn, True),
    scenarios=[
        weather, dust, travel, restaurant
    ]
)


@kochat.app.route('/')
def index():
    return render_template("index.html")


if __name__ == '__main__':
    kochat.app.template_folder = kochat.root_dir + 'templates'
    kochat.app.static_folder = kochat.root_dir + 'static'
    kochat.app.run(port=8080, host='0.0.0.0')



7.3. ์‹œ๋‚˜๋ฆฌ์˜ค ๊ตฌ์„ฑ

Kocrawl์„ ์ด์šฉํ•ด 4๊ฐ€์ง€ ์˜๋„์— ๋งž๋Š” ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ๊ตฌ์„ฑํ•˜์˜€์Šต๋‹ˆ๋‹ค.

weather = Scenario(
    intent='weather',
    api=WeatherCrawler().request,
    scenario={
        'LOCATION': [],
        'DATE': ['์˜ค๋Š˜']
    }
)

dust = Scenario(
    intent='dust',
    api=DustCrawler().request,
    scenario={
        'LOCATION': [],
        'DATE': ['์˜ค๋Š˜']
    }
)

restaurant = Scenario(
    intent='restaurant',
    api=RestaurantCrawler().request,
    scenario={
        'LOCATION': [],
        'RESTAURANT': ['์œ ๋ช…ํ•œ']
    }
)

travel = Scenario(
    intent='travel',
    api=MapCrawler().request,
    scenario={
        'LOCATION': [],
        'PLACE': ['๊ด€๊ด‘์ง€']
    }
)



7.4. Javascript ๊ตฌํ˜„ (+ Ajax)

๋งˆ์ง€๋ง‰์œผ๋กœ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ๋ฉ”์‹œ์ง€๊ฐ€ ๋„์›Œ์ง€๋Š” ์• ๋‹ˆ๋ฉ”์ด์…˜๊ณผ Ajax๋ฅผ ํ†ตํ•ด Kochat ์„œ๋ฒ„์™€ ํ†ต์‹ ํ•˜๋Š” ์†Œ์Šค์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜์˜€์Šต๋‹ˆ๋‹ค. ๊ฐ„๋‹จํ•œ chit chat ๋Œ€ํ™” 3๊ฐ€์ง€ (์•ˆ๋…•, ๊ณ ๋งˆ์›Œ, ์—†์–ด)๋Š” ๊ทœ์น™๊ธฐ๋ฐ˜์œผ๋กœ ๊ตฌํ˜„ํ•˜์˜€์Šต๋‹ˆ๋‹ค. ์ถ”ํ›„์— Seq2Seq ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•˜์—ฌ ์ด ๋ถ€๋ถ„๋„ ๋จธ์‹ ๋Ÿฌ๋‹ ๊ธฐ๋ฐ˜์œผ๋กœ ๋ณ€๊ฒฝํ•  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.

// variables
let userName = null;
let state = 'SUCCESS';

// functions
function Message(arg) {
    this.text = arg.text;
    this.message_side = arg.message_side;

    this.draw = function (_this) {
        return function () {
            let $message;
            $message = $($('.message_template').clone().html());
            $message.addClass(_this.message_side).find('.text').html(_this.text);
            $('.messages').append($message);

            return setTimeout(function () {
                return $message.addClass('appeared');
            }, 0);
        };
    }(this);
    return this;
}

function getMessageText() {
    let $message_input;
    $message_input = $('.message_input');
    return $message_input.val();
}

function sendMessage(text, message_side) {
    let $messages, message;
    $('.message_input').val('');
    $messages = $('.messages');
    message = new Message({
        text: text,
        message_side: message_side
    });
    message.draw();
    $messages.animate({scrollTop: $messages.prop('scrollHeight')}, 300);
}

function greet() {
    setTimeout(function () {
        return sendMessage("Kochat ๋ฐ๋ชจ์— ์˜ค์‹ ๊ฑธ ํ™˜์˜ํ•ฉ๋‹ˆ๋‹ค.", 'left');
    }, 1000);

    setTimeout(function () {
        return sendMessage("์‚ฌ์šฉํ•  ๋‹‰๋„ค์ž„์„ ์•Œ๋ ค์ฃผ์„ธ์š”.", 'left');
    }, 2000);
}

function onClickAsEnter(e) {
    if (e.keyCode === 13) {
        onSendButtonClicked()
    }
}

function setUserName(username) {

    if (username != null && username.replace(" ", "" !== "")) {
        setTimeout(function () {
            return sendMessage("๋ฐ˜๊ฐ‘์Šต๋‹ˆ๋‹ค." + username + "๋‹˜. ๋‹‰๋„ค์ž„์ด ์„ค์ •๋˜์—ˆ์Šต๋‹ˆ๋‹ค.", 'left');
        }, 1000);
        setTimeout(function () {
            return sendMessage("์ €๋Š” ๊ฐ์ข… ์—ฌํ–‰ ์ •๋ณด๋ฅผ ์•Œ๋ ค์ฃผ๋Š” ์—ฌํ–‰๋ด‡์ž…๋‹ˆ๋‹ค.", 'left');
        }, 2000);
        setTimeout(function () {
            return sendMessage("๋‚ ์”จ, ๋ฏธ์„ธ๋จผ์ง€, ์—ฌํ–‰์ง€, ๋ง›์ง‘ ์ •๋ณด์— ๋Œ€ํ•ด ๋ฌด์—‡์ด๋“  ๋ฌผ์–ด๋ณด์„ธ์š”!", 'left');
        }, 3000);

        return username;

    } else {
        setTimeout(function () {
            return sendMessage("์˜ฌ๋ฐ”๋ฅธ ๋‹‰๋„ค์ž„์„ ์ด์šฉํ•ด์ฃผ์„ธ์š”.", 'left');
        }, 1000);

        return null;
    }
}

function requestChat(messageText, url_pattern) {
    $.ajax({
        url: "http://0.0.0.0:8080/" + url_pattern + '/' + userName + '/' + messageText,
        type: "GET",
        dataType: "json",
        success: function (data) {
            state = data['state'];

            if (state === 'SUCCESS') {
                return sendMessage(data['answer'], 'left');
            } else if (state === 'REQUIRE_LOCATION') {
                return sendMessage('์–ด๋Š ์ง€์—ญ์„ ์•Œ๋ ค๋“œ๋ฆด๊นŒ์š”?', 'left');
            } else {
                return sendMessage('์ฃ„์†กํ•ฉ๋‹ˆ๋‹ค. ๋ฌด์Šจ๋ง์ธ์ง€ ์ž˜ ๋ชจ๋ฅด๊ฒ ์–ด์š”.', 'left');
            }
        },

        error: function (request, status, error) {
            console.log(error);

            return sendMessage('์ฃ„์†กํ•ฉ๋‹ˆ๋‹ค. ์„œ๋ฒ„ ์—ฐ๊ฒฐ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค.', 'left');
        }
    });
}

function onSendButtonClicked() {
    let messageText = getMessageText();
    sendMessage(messageText, 'right');

    if (userName == null) {
        userName = setUserName(messageText);

    } else {
        if (messageText.includes('์•ˆ๋…•')) {
            setTimeout(function () {
                return sendMessage("์•ˆ๋…•ํ•˜์„ธ์š”. ์ €๋Š” Kochat ์—ฌํ–‰๋ด‡์ž…๋‹ˆ๋‹ค.", 'left');
            }, 1000);
        } else if (messageText.includes('๊ณ ๋งˆ์›Œ')) {
            setTimeout(function () {
                return sendMessage("์ฒœ๋งŒ์—์š”. ๋” ๋ฌผ์–ด๋ณด์‹ค ๊ฑด ์—†๋‚˜์š”?", 'left');
            }, 1000);
        } else if (messageText.includes('์—†์–ด')) {
            setTimeout(function () {
                return sendMessage("๊ทธ๋ ‡๊ตฐ์š”. ์•Œ๊ฒ ์Šต๋‹ˆ๋‹ค!", 'left');
            }, 1000);


        } else if (state.includes('REQUIRE')) {
            return requestChat(messageText, 'fill_slot');
        } else {
            return requestChat(messageText, 'request_chat');
        }
    }
}



7.5. ์‹คํ–‰ ๊ฒฐ๊ณผ



Warning

๋ฐ๋ชจ ๋ฐ์ดํ„ฐ์…‹์€ ์–‘์ด ์ ๊ธฐ ๋•Œ๋ฌธ์— ๋‹ค์–‘ํ•œ ์ง€๋ช…์ด๋‚˜ ๋‹ค์–‘ํ•œ ์Œ์‹, ๋‹ค์–‘ํ•œ ์—ฌํ–‰์ง€ ๋“ฑ์€ ์•Œ์•„ ๋“ฃ์ง€ ๋ชปํ•ฉ๋‹ˆ๋‹ค. (๋ฐ๋ชจ์˜์ƒ์„ ์œ„ํ•ด ์ผ๋ถ€ ์„œ์šธ ์ง€์—ญ ์œ„์ฃผ๋กœ๋งŒ ๋ฐ์ดํ„ฐ์…‹์„ ์ž‘์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค.) ๋ฐ๋ชจ๋ฐ์ดํ„ฐ์…‹์€ ๋ฐ๋ชจ์˜์ƒ์„ ์ฐ๊ธฐ ์œ„ํ•œ ์•„์ฃผ ์ž‘์€ dev ๋ฐ์ดํ„ฐ ์…‹์ž…๋‹ˆ๋‹ค. ์‹ค์ œ๋กœ ๋‹ค์–‘ํ•œ ๋„์‹œ๋‚˜ ๋‹ค์–‘ํ•œ ์Œ์‹ ๋“ฑ์„ ์•Œ์•„ ๋“ค์„ ์ •๋„๋กœ ๋Œ€ํ™”๋ฅผ ๋‚˜๋ˆ„๋ ค๋ฉด ๋ฐ๋ชจ ๋ฐ์ดํ„ฐ์…‹๋ณด๋‹ค ์ž์ฒด์ ์ธ ๋ฐ์ดํ„ฐ ์…‹์„ ๋งŽ์ด ์‚ฝ์ž…ํ•˜์…”์•ผ ๋”์šฑ ์ข‹์€ ์„ฑ๋Šฅ์„ ๊ธฐ๋Œ€ํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ํ•˜๋ฃจ๋นจ๋ฆฌ Pretrain ๋ชจ๋ธ์„ ์ง€์›ํ•˜์—ฌ ์ด๋Ÿฌํ•œ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ๋ชจ๋“  ๋ฐ๋ชจ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์†Œ์Šค์ฝ”๋“œ๋Š” ์—ฌ๊ธฐ ๋ฅผ ์ฐธ๊ณ ํ•ด์ฃผ์„ธ์š”


8. Contributor

๋งŒ์•ฝ ๋ณธ์ธ์ด ์›ํ•˜๋Š” ๊ธฐ๋Šฅ์„ Kocchat์— ์ถ”๊ฐ€ํ•˜๊ณ  ์‹ถ์œผ์‹œ๋‹ค๋ฉด ์–ธ์ œ๋“ ์ง€ ์ปจํŠธ๋ฆฌ๋ทฐ์…˜ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


9. TODO List

  • ver 1.0 : ์—”ํ‹ฐํ‹ฐ ํ•™์Šต์— CRF ๋ฐ ๋กœ์Šค ๋งˆ์Šคํ‚น ์ถ”๊ฐ€ํ•˜๊ธฐ
  • ver 1.0 : ์ƒ์„ธํ•œ README ๋ฌธ์„œ ์ž‘์„ฑ ๋ฐ PyPI ๋ฐฐํฌํ•˜๊ธฐ
  • ver 1.0 : ๊ฐ„๋‹จํ•œ ์›น ์ธํ„ฐํŽ˜์ด์Šค ๊ธฐ๋ฐ˜ ๋ฐ๋ชจ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ œ์ž‘ํ•˜๊ธฐ
  • ver 1.0 : Jupyter Note Example ์ž‘์„ฑํ•˜๊ธฐ + Colab ์‹คํ–‰ ํ™˜๊ฒฝ
  • ver 1.1 : ๋ฐ์ดํ„ฐ์…‹ ํฌ๋งท RASA์ฒ˜๋Ÿผ markdown์— ๋Œ€๊ด„ํ˜ธ ํ˜•ํƒœ๋กœ ๋ณ€๊ฒฝ
  • ver 1.2 : Pretrain Embedding ์ ์šฉ ๊ฐ€๋Šฅํ•˜๊ฒŒ ๋ณ€๊ฒฝ (Gensim)
  • ver 1.3 : Transformer ๊ธฐ๋ฐ˜ ๋ชจ๋ธ ์ถ”๊ฐ€ (Etri BERT, SK BERT)
  • ver 1.3 : Pytorch Embedding ๋ชจ๋ธ ์ถ”๊ฐ€ + Pretrain ์ ์šฉ ๊ฐ€๋Šฅํ•˜๊ฒŒ
  • ver 1.4 : Seq2Seq ์ถ”๊ฐ€ํ•ด์„œ Fallback์‹œ ๋Œ€์ฒ˜ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋งŒ๋“ค๊ธฐ (LSTM, SK GPT2)
  • ver 1.5 : ๋„ค์ด๋ฒ„ ๋งž์ถค๋ฒ• ๊ฒ€์‚ฌ๊ธฐ ์ œ๊ฑฐํ•˜๊ณ , ์ž์ฒด์ ์ธ ๋„์–ด์“ฐ๊ธฐ ๊ฒ€์‚ฌ๋ชจ๋“ˆ ์ถ”๊ฐ€
  • ver 1.6 : BERT์™€ Markov ์ฒด์ธ์„ ์ด์šฉํ•œ ์ž๋™ OOD ๋ฐ์ดํ„ฐ ์ƒ์„ฑ๊ธฐ๋Šฅ ์ถ”๊ฐ€
  • ver 1.7 : ๋Œ€ํ™” ํ๋ฆ„๊ด€๋ฆฌ๋ฅผ ์œ„ํ•œ Story ๊ด€๋ฆฌ ๊ธฐ๋Šฅ ๊ตฌํ˜„ํ•ด์„œ ์ถ”๊ฐ€ํ•˜๊ธฐ


10. Reference

11. License

Copyright 2020 Kochat.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.