LG Aimers에서 KAIST 차미영 교수님 강의를 듣고 정리한 내용입니다.

틀린 부분이 있다면 댓글 부탁드립니다!

 


 

1. 인공지능과 Creativity

(1) AI도 예술을 창조하는 시대가 도래

- 생성형 AI를 기반으로 그림을 만들고, 작곡을 하기도 한다.

- 실제로 Q&A 시스템에서 인공지능이 인간의 성능을 초월하기도 했다.

[Rajpurkar-Jia-Liang, ACL 2018]

- 2018년 기준이므로 현재는 더 뛰어난 모델이 개발되었을 것이다..

 

 

- 2024년 부천국제판타스틱 영화제에서는 인공지능 영화 부문을 신설하기도 했다.

 

 

 

(2) 저작권 이슈

https://obvious-art.com/portfolio/edmond-de-belamy/

 

- $432,500의 가격에 팔린 인공지능이 만들어낸 작품이다.

- 이때, 학습 데이터, 프로그래머, 기획자 사이에서 저작권 이슈는 빼놓을 수 없다.

- AI 예술 작품은 학습 데이터 기반이기 때문에 '창작성 (originality)'가 있는지 의문이 들기도 한다.

 

- 학습에 사용된 데이터를 제공한 사람에게도 혜택이 돌아가기가 어려운 것이 사실이다.

- 창작자인 AI는 법적 권리르 제공할 수 있는 법적 제도가 부재한 상황이며, 현존하는 예술가들의 스타일을 따라한 경우 상업적 피해를 발생시킨다.

- 또한, 창작된 작품이 인간의 윤리적 규범을 따르지 않을 수도 있다.

 

 

 

(3) 인공지능 법인

- 법인은 법적 권리와 의무에 대한 책임을 소지한다.

- 인공지능은 아직 법인에 속하지 않는다.

 

- '인공지능을 법인으로 인정할 것인가?'에 대한 의논이 벌어지기도 했다.

 

"장기적으로 로봇에 대한 구체적인 법적 지위를 창출하여, 적어도 가장 정교한 자율주행 로봇이 야기할 수 있는 손해를 충분히 배상할 책임이 있는 전자인의 지위를 갖게 하고, 로봇이 자율적인 의사결정을 하거나 그 밖에 독립적으로 제3자와 상호작용하는 경우에 전자인격을 적용할 가능성이 있는 경우"

 

 

- 법인격의 장점으로는 (1) 책임 문제가 쉽게 해결 (2) 혁신, 사회 발전 가능 (3) 법제도가 일관성 있게 유지 (4) 자연인이 아닌 것에 대한 법인이 이미 존재함

- 법인격의 단점으로는 (1) 법적 처벌의 도피로 악용될 가능성 (Liability Shields) (2) 도구주의에 반함 (3) 인류에 대한 수치 (4) 처벌의 어려움

 

 

 

(4) Moral Machine

- 아시모프의 로봇 3원칙이 있다.

- 첫째, 로봇은 인간을 다치게 해선 안 되며, 인간이 해를 입는 걸 방관해서도 안 된다.

- 둘째, 법칙 1에 위배되지 않는 한, 로봇은 인간의 명령에 복종한다.

- 셋째, 법칙 1과 2에 위배되지 않는 한, 로봇은 스스로를 보호해야 한다.

 

 

- 그렇다면 인공지능으로 인한 부작용에 책임 소재는 누구에게 있는지가 문제이다.

- 자율주행 자동차에서 사고가 발생했다면, 소유자, 자동차 회사, AI 개발자, 운전자, 자율주행차, 보행자 중에서 누구에게 법적 책임이 있는 것일까?

https://en.wikipedia.org/wiki/Moral_Machine

 

 

- 알고리즘이 결정을 내릴 때, Moral Machine 설문을 통해 선호도를 측정한다고 한다.

 

http://moralmachine.mit.edu/

 

- 사고의 가능성은 항상 존재하며, 누구를 보호할 것인가에 대한 인공지능의 결정은 여전히 연구 주제이다.

 

 

 

 

2. 세계적인 데이터 과학자가 되는 방법

- 이종 데이터의 결합을 통해 혁신적인 결과를 낸다.

- 예를 들어, 서울시 야간 버스 노선을 제작할 때 통신사와의 협업을 통해 수요를 파악했다고 한다.

 

- Digital Transformation을 통해, 이종 빅데이터의 결합과 새로운 인공지능 기반 계산 과학 방법의 적용이 이뤄진다.

- 이를 통해 데이터 사이언스 기반 난제를 해결하고, 정책을 결정하며 신규 산업을 창출할 수 있을 것으로 기대된다.

 

- 페이스북 스타일로 목표를 설정하는데, 이는 목표가 너무 낮아서 빨리 성취하도록 하는 것을 방지한다. 계속된 목표 수정으로 계속해서 성장해 나가도록 한다.

 

 


 

1. 인간의 창조적 활동 영역으로 들어온 인공지능은 인간의 개입 없이 독자적 창작과 혁신 활동이 가능한 수준으로 발전할 것이라는 전망이 있다.

 

2. AI 시대의 지식 재산, 법인격, 처벌, 윤리 문제 부각은 다양한 계층 시민의 수요와 요구를 논의를 통해 반영해야 한다.

 

3. AI에 대한 경계와 규제의 선택은 인류에 대한 재정의라고 할 수 있다.

LG Aimers에서 KAIST 차미영 교수님 강의를 듣고 정리한 내용입니다.

틀린 부분이 있다면 댓글 부탁드립니다!

 


 

1. 데이터의 시대

(1) 데이터를 잘 해석하고 있는가?

NEJM 2012, “Chocolate Consumption, Cognitive Function, and Nobel Laureates”

 

- '초콜릿을 많이 먹으면 노벨상을 탄다'는 말은 맞는 말일까? 좀 의아하게 들린다. 따라서 데이터를 잘 해석해야 한다.

 

- '상관관계' ≠ '인과관계'

 

 

 

(2) 데이터 전처리와 분석 방법은 적절한가?

 

- 실제로 메타에서는 그래프에서 Error bar가 존재하지 않는다면 아예 데이터를 믿지 않는다고 한다.

- 따라서, (1) Error bar 추가 (2) 적절한 통계 테스트 찾기 (3) Outlier 제거 (4) 데이터 표준화 (5) EDA를 통해 데이터를 충분히 들여다보고 알아나가는 시간이 필요하다.

 

 

 

(3) 학습에 쓰는 데이터가 충분한가?

 

- 일반적으로 인공지능 알고리즘에서 필요한 데이터 양은 보통 million scale (100만 데이터)라고 한다.

 

- 데이터 학습의 결과가 적절한 수준인지에 대한 인식이 있어야 한다. 그림처럼 모델이 너무 단순하거나, 너무 복잡하여 Under-fitting 되거나 Over-fitting 되지 않도록 해야 한다.

 

- 학습 데이터 ≠ 테스트 데이터

 

 

 

2. Black Box Algorithm

(1) 설명력이 중요한 AI

- 인공지능에서는 '성능' 뿐만 아니라, '설명력'도 중요한 요소이다.

- 실제로 면세 범위 초과 물품, 위장 반입, 원산지 조작 등의 세관에서 벌어지는 불법 행위를 적발하는 AI 기술을 개발한 것에 대해 왜 그렇게 판단을 하였는지에 대한 '설명력'이 전문가들을 설득하는 데 중요한 요소로 작용했다고 한다.

 

 

- AI 기반 학습 알고리즘은 결과에 대한 설명이 불가능한 'Black Box Algorithm'이라는 특징을 가지고 있다.

- 앞선 사례처럼 High risk 결정에서는 설명력도 정확도 (성능) 만큼이나 중요하다.

 

- post-hoc explainability (사후 설명력)

예시) Saliency Map (어디에 집중해서 보는가?), SHAP (SHapley Additive exPlanations, 게임 이론을 바탕으로 각 player의 기여분 계산)

 

- 알고리즘의 내면을 가시화하여 보여주는 기술이 개발되고 있다.

 

 

 

(2) 학습 결과가 바뀔 수 있는 위험성

https://hackaday.com/2018/04/15/one-pixel-attack-fools-neural-networks/

 

- One Pixel attack의 예시이다.

- 픽셀 하나만 바뀌었지만 알고리즘 학습 결과가 달라지는, 노이즈에 굉장히 민감한 모델이다.

- 충분히 신뢰성 있는 모델을 개발하도록 해야 한다.

 

 

 

3. Handling the Web data

(1) 의견의 대표성

'Spiral of Silence (침묵의 나선)'

 

https://www.communicationtheory.org/the-spiral-of-silence-theory/

 

- 이는 각 개인들이 '고립'에 대한 공포가 있으며, 고립에 대한 공포는 자신의 의견을 말하는 것보다 침묵을 유지하는 방향으로 이끈다는 것이다.

- 특정 의견이 다수의 사람들에게 지배적인 의견으로 인정되고 있다면, 반대되는 의견을 가지고 있는 소수의 사람들은 침묵하려는 경향이 크다고 한다.

 

- 이러한 현상은 SNS를 통한 빠른 정보 전파, bot의 참여, 극단화 현상 주의를 야기한다. 즉, 정보의 편향을 일으킨다.

 

 

 

(2) 인포데믹 현상

- 오정보의 빠른 확산으로 인한 인포데믹 (infodemic) 현상이 발생한다.

- 사실 정보와 더불어 오정보의 양이 늘어, 구분이 어려워지는 정보 과부하 현상이다.

[Ma et al., IJCAI 2016, Kwon et al., PLoSOne 2017, Bail et al., PNAS 2018, Vosoughiet al., Science 2018, Kimetal.,WSDM2018]

- Facts는 연결되면서 퍼지지만, Rumors는 산발적으로 퍼져나가며 인포데믹 현상을 발생시킨다.

 

 

 

(3) Digital Services Act

- 유럽 연합 중심으로 빅테크 기업 대상 플랫폼 유해 컨텐츠 단속 의무를 강화하고 있으며, 이는 전세계로 확산되고 있다.

- 네티즌의 성별, 인종, 종교 등에 기반한 알고리즘으로 개인화 추천 광고를 노출하지 않도록 하고 있다.

- DSA 위반 시 연매출 대비 상당한 과징금도 부여하고 있다.

 

 

 

3. AI and Ethical Decisions

(1) COMPAS (Correctional Offender Management Profiling for Alternative Sanctions) 

- North pointe, Inc가 개발한 소프트웨어로, 피고의 미래 범죄 위험을 점수로 예측한다.

- 미국 법원의 형사 재판에서 판사들의 의사결정을 지원하기 위해 사용하고 있으며, 현재 캘리포니아주, 뉴욕주, 위스콘신주,  플로리다, 워싱턴 등 12개 기타 관할권 법원에서 사용중이다.

 

 

- 한편, COMPAS가 예측한 미래 범죄 위험도 알고리즘이 인종 차별을 조장하는 사례가 있었다.

 

- 두 사람 모두 사소한 절도로 인해 체포되었다.

- 왼쪽은 무장 강도 등의 전과가 있었고, 오른쪽은 어린 시절 저지른 경범죄의 전과가 있었다. 하지만, 흑인 여성에 대해 COMPAS는 더 높은 범죄 위험도를 부여했다.

- 2년 후, 왼쪽은 절도 혐의로 징역을 살게 되었고, 오른쪽은 이후 범죄를 저지르지 않았다고 한다.

 

- 알고리즘의 '편향성' 때문에 발생한 사례이다.

 

 

 

(2) Microsoft Tay

- 백인 우월주의자, 여성, 무슬림 혐오자들이 차별 발언하도록 학습되어 공개 16시간만에 서비스를 중단했다.

- 이후 MS는 Zo라는 13세 여성 캐릭터 챗봇을 새로 공개했는데, 소수자에 대한 대화 자체를 피하도록 개발되어 컨텐츠 검열이라는 지적을 받기도 했다.

 

 


 

1. 데이터의 확보, 전처리, 분석, 해석의 전 과정이 중요하다. 데이터가 알고리즘의 품질을 결정한다.

 

2. 알고리즘의 설명력, 편향, 신뢰의 문제에 주의한다.

1. 문제 설명

n개의 음이 아닌 정수들이 있다. 이 정수들을 순서를 바꾸지 않고 적절히 더하거나 빼서 타겟 넘버를 만들려고 한다.

예를 들어 [1, 1, 1, 1, 1]로 숫자 3을 만들려면 다음 다섯 방법을 쓸 수 있다.

 

(1) -1+1+1+1+1 = 3

(2) +1-1+1+1+1 = 3

(3) +1+1-1+1+1 = 3

(4) +1+1+1-1+1 = 3

(5) +1+1+1+1-1 = 3

 

사용할 수 있는 숫자가 담긴 배열 numbers, 타겟 넘버 target이 매개변수로 주어질 때 숫자를 적절히 더하고 빼서 타겟 넘버를 만드는 방법의 수를 반환하라.

 

 

 

2. 코드

answer = 0

def DFS(result, numbers, target, idx):
    global answer
    
    if idx == len(numbers):
        if result == target:
            answer += 1
        return

    DFS(result + numbers[idx], numbers, target, idx + 1)
    DFS(result - numbers[idx], numbers, target, idx + 1)
    
def solution(numbers, target):
    global answer
    DFS(0, numbers, target, 0)
    
    return answer

 

 

 

3. 설명

전형적인 깊이 우선 탐색 (DFS) 문제이다.

 

우선 solution에서 numbers 배열과 target, 시작하는 인덱스 번호인 0, 빼고 더한 결과를 저장하는 result에 0을 담아서 DFS 함수에 전달한다.

전역변수 (global) answer를 사용해서, 만약 numbers의 모든 원소를 다 한 번씩 더하거나 뺐고 그 결과가 타겟 넘버와 같다면 answer을 1 키우고 함수를 종료한다.

 

그렇지 않은 경우에는 현재 인덱스의 원소를 더한 값, 뺀 값을 각각 재귀함수로 호출하여 연산을 수행한다.

1. 문제 설명

전화번호부에 적힌 전화번호 중, 한 번호가 다른 번호의 접두어인 경우가 있는지 확인하려고 한다.

 

전화번호가 다음과 같은 경우, 구조대 전화번호는 영석이의 전화번호의 접두사이다.

- 구조대 : 119

- 박준영 : 97674223

- 지영석 : 1195524421

 

전화번호부에 적힌 전화번호를 담은 배열 phone_book이 solution 함수의 매개변수로 주어질 때, 어떤 번호가 다른 번호의 접두어인 경우가 있으면 false를 그렇지 않으면 true를 반환하라.

 

 

2. 코드

(1) 1차 코드 - 효율성 테스트 3, 4 실패 (시간 초과)

def solution(phone_book):
    phone_book = sorted(phone_book, key=len)

    for phone in phone_book:
        temp = phone_book.copy()
        temp.remove(phone)

        for t in temp:
            n = len(phone)
            
            if phone == t[:n]:
                return False
    return True

 

def solution(phone_book):
    phone_book = sorted(phone_book, key=len)

    for i, phone in enumerate(phone_book):
        
        for j, t in enumerate(phone_book):
            n = len(phone)
            
            if j != i:
                if phone == t[:n]:
                    return False
    return True

 

 

 

(2) zip, startswith 사용한 코드

def solution(phone_book):
    phone_book = sorted(phone_book)

    for p1, p2 in zip(phone_book, phone_book[1:]):
        if p2.startswith(p1):
            return False
    return True

 

 

 

(3) Hash Map 사용한 코드

def solution(phone_book):
    hash_map = {}

    for phone_number in phone_book:
        hash_map[phone_number] = 1

    for phone_number in phone_book:
        temp = ""

        for number in phone_number:
            temp += number
            
            if temp in hash_map and temp != phone_number:
                return False
    return True

 

 

 

 

3. 설명

(1) 1차 코드

- 전화 번호 길이를 기준으로 배열을 sorting하고, phone_book에 있는 전화 번호 하나씩 검토하면서 t[:n]과 배열의 전화번호가 일치하는지 비교한다.

- 아무래도 sorting 후에 2중 for문을 해서 그런지, 시간 초과가 계속 떴다.

 

 

(2) zip, startswith 사용한 코드

- 숫자로 되어 있는 문자열을 sorting하면, 왼쪽에서부터 크기 순서대로 배열된다.

예를 들어, ['119', '9767423', '1195524421']은 ['119', '1195524421', '97674223'] 이렇게 정렬된다.

 

- 따라서, zip(phone_book, phone_book[1:])로 그 다음 원소와 비교하면서 다음 원소가 이전 원소로 시작되는지 startswith()로 검토한다.

 

 

(3) Hash Map 사용한 코드

- 우선 Hash Map을 사용한 이유는, 리스트보다 원소의 존재를 파악하는데 더 낮은 복잡도를 가지고 있기 때문이다. (O(1))

 

- 전화번호를 hash_map에 key값으로 다 저장해둔다.

- 짧은 문자열 통째로 긴 문자열의 앞쪽과 비교하는 것이 아니라, 긴 문자열의 앞쪽부터 하나씩 temp에 더해가며 짧은 문자열과 동일한 요소가 있는지 확인하는 방식이다.

(나는 문자열 통째로를 긴 문자열의 앞쪽과 비교할 생각만 했어서.. 관점의 변화를 확 느낄 수 있었던 코드였다.)

 

- 원소의 개수를 파악하기 위해서도 Hash map이 사용되지만, 특정 원소의 존재를 확인하는데 빠른 속도를 가지고 있다는 장점으로 다양한 문제에서도 활용 가능하다고 생각한다!

 

5월 14일에 마곡 LG사이언스파크에서 진행한 LG Aimers 4기 Mentoring Day에 참석했다.

LG Aimers 4기 활동 참여자 중에서 이메일 폼 작성 선착순으로 참석할 수 있었는데, 운이 좋게도 메일을 일찍 봐서 선착순 안에 들 수 있었다.

 

 

집이랑 마곡이 20분 거리라서 종종 마곡에 놀러갔다가 LG 건물을 보곤 했는데, 직접 들어가보니 생각보다 더 삐까뻔쩍했다..ㅎㅎ 지어진지 오래되지 않아서 그런지 빌딩이 매우 깨끗했다. 1층 로비에서는 LG Twins의 우승을 기원하는 행사를 하고 계셨다ㅋㅋㅋ

 

 

멘토링 데이의 순서는 다음과 같다.

 

총 세 분의 LG AI 연구원 분들이 발표를 해주셨다.

각각 Material Intelligence Lab, Multimodal Lab, Data Intelligence Lab에 계신 분들이었다.

 

세미나 내용이 매우 흥미로웠고, 국내 대기업이 어떻게 인공지능을 연구하고 자사에 활용하는지 알 수 있었던 시간이었다.

발표해주신 내용을 문제가 되지 않는 선에서 정리해보려고 한다.

(혹시 문제가 있거나.. 틀린 내용이 있다면 댓글 부탁드립니다!)

 

 

 

1. Material Intelligence Lab

연구원 분께서는 생물학 전공을 하셨고, LG에서는 융복합 연구를 진행하고 계셨다.

그 중에서도 '신약 생성 모델'과 '개인화 암 백신 개발'에 대한 내용을 말씀해주셨다.

 

(1) 신약 생성 모델 (Drug Generative Model)

새로운 약을 개발할 때 시간이 매우 많이 소요된다. 평균 15년을 잡고 개발비도 굉장히 많이 들어간다고 한다.

신약 하나를 개발하는 데 약 10,000번의 실험이 필요하다고 하는데..

그럼에도 계속해서 신약 개발을 시도하는 이유는 성공 시에 계속 매출을 발생하기 때문이다.

 

 

신약 생성 과정은

- Target 발굴 ⭢ Hit 발굴 ⭢ Lead 발굴 및 최적화 ⭢ 임상 과정

이렇게 이루어진다.

출처 : https://news.nate.com/view/20230213n00505

 

'Target'은 질병의 원인으로 주로 단백질이다.

'Hit'는 단백질 결합 분자이며,

'Lead 발굴 및 최적화' 과정에서는 질병의 원인 단백질과 잘 결합하며 독성이 없고, 안전하고 안정성 있는 Lead를 발견한다.

 

이 중에서 'Lead 발굴 및 최적화'에서 사람이 직접 하나하나 실험하는 것을 줄이기 위해 인공지능을 도입한다.

사용 모델까지도 상세하게 설명해주셨는데, 그 부분까지는 언급하면 안 될 것 같아서 최대한 러프하게 정리해보겠다.

 

'이기적 유전자'로 유명한 영국의 진화론자 리처드 도킨스가 "컴퓨터 프로그램에게 노벨상을 줄 수 있다면..." 말과 함께 알파폴드 기사를 링크함

 

 

 

신약 '생성' 모델이기 때문에 특정 약물 타겟과 결합할 수 있으며 안정성, 안전성이 양호한 약물을 생성하는 과정을 모델링하였다.

이 과정에서의 어려운 점은 특정 약물 타겟과의 결합 안정성이 있는지 없는지에 관한 데이터가 부족하다는 것이었다.

이를 타개하기 위해!

(1) 기존에 알려진 약물과의 유사성(2) 예측 효능을 바탕으로 리워드를 제공하고 최적화하였다.

 

이후에는 Domain 연구원 분들이 특정 기준에 따라서 모델링 결과를 필터링 후 직접 실험을 통해 실제로 유효한 약물 후보를 도출한다고 한다.

 

 

결과적으로 Lead 물질 개발 시간을 약 8년에서 3.5년으로 대폭 줄였다는 의의가 있다.

또한, 해당 연구의 시사점으로

(1) 공개된 기술과 현업에서의 간극 (대표적으로는 데이터의 현황이 있다.)(2) 도메인 전문가와의 계속 협업이 필요하다는 점이 있다.

 

 

 

(2) 개인화 암 백신 개발 (Personalized Cancer Vaccine)

암은 몸에 있던 유전자에 변이가 생기고 비정상적인 세포가 생기며, 그러한 세포들이 증식하면서 발병한다.

 

 

기존의 항암제는

- 1세대 : 암세포 말고도 다른 정상 세포 공격 ⭢ 부작용이 매우 큰 방식

- 2세대 : 독성 물질을 암세포에만 targeting ⭢ 부작용이 0%라고 말할 수는 없으며 재발 가능성 있음

- 3세대 : 인체의 면역 시스템을 활용 ⭢ 암세포가 만들어지기 전, 우리 몸에 있지 않은 단백질에 대해 포획하여 제거하기

 

3세대의 항암제를 개발하면 부작용과 재발 가능성을 줄일 수 있다.

하지만, 어려운 점은 암 환자마다 변이된 부분이 다르다는 것이다. 따라서, '개인화'가 필요하다.

출처 : https://www.cancer.go.kr/lay1/S1T289C290/contents.do

 

우리 몸의 어떤 세포가 생산한 단백질이 조각나고, MHC (Major Histocompatibility Complex)라는 단백질에 의해서 세포 밖으로 나온다.

세포 밖으로 나온 단백질 조각을 T세포가 인식하고, 원래 있던 단백질이라면 지나치게, 그렇지 않다면 항원으로 인식해 세포를 공격한다.

 

여기에서 아이디어를 얻어, 신항원을 예측하여 인위적으로 넣어준다면 암에 대한 면역원성을 증가시킬 수 있다는 결론을 냈다. 암을 우리 몸의 내부적인 면역 세포로 치료 가능한 것이다.

 

 

 

하지만, 사람마다 DNA가 다르고 가지수가 매우 다양하기 때문에 어려운 점이 있다.

사람마다 MHC Binding 분포가 다르다는데, 이는

(1) MHC 분자와 단백질 펩타이드가 결합을 하는지, 하지 않는지에 대한 것(2) 이 결합된 것이 T세포 수용체와 결합을 하는지, 하지 않는지에 대한 문제이다.

 

 

LG AI 연구원에서는 환자에게서 암 조직과 정상 조직을 받아 DNA를 분석하고, 이 사람이 가진 MHC 타입을 알아내었다. 또한, 정상 조직과 그렇지 않은 조직의 비교를 통해 어떤 부분이 변이 되었는지를 판단하여 초기 후보로 두었다.

 

 

 

실제로 NeurlPS 2022에서 Attention 기반의 MHC Binding 특성 모델링으로 accept 되었다고 한다.

결과적으로 펩타이드의 위치 및 아미노산 종류별 Attention score를 확인 및 시각화할 수 있게 되었다.

 

 

 

사실 이런 바이오 쪽 도메인 지식이 없다보니.. 충분히 쉽게 설명해주셨음에도 이해되지 않는 부분이 많았다.

요약을 해보면 다음과 같다.

 

바이오 분야에서 인공지능 융합은 '데이터 분석'과 '큐레이션'이 중요하다고 하셨다.

학습에 사용되는 데이터는 굉장히 많고 다양한데, 각 논문마다 실험 목적과 방법이 다르기 때문에 데이터베이스 선별이 매우 중요하다고 한다.

직접 실험을 따라하면서 해당 논문의 실험 과정과 결과 도출을 완벽히 이해한 상태가 필요한 것이다!

 

이쪽 분야에서는 아직 벤치마크가 활발하게 정리된 상황이 아니라서 아직 발전 가능성이 무궁무진한 분야라고 한다. 실제로 많은 해외 제약회사에서는 이러한 방식으로 신약 개발과 의료 개발이 이뤄지고 있다고 한다.

인공지능 활용의 대표적인 모범 사례인 것 같다.

 

 

 

 

2. Multimodal Lab

이미지-텍스트의 멀티모달리티 연구의 주제로는 다양한데,

(1) Image Generation : 텍스트를 입력했을 때 이미지 생성 (예. Dalle-3, Stable diffusion-3)

(2) Image Understanding : 이미지를 넣었을 때 이미지를 해석. 사용자에게 정보 제공 (예. GPT-4, LLaVA)

 

 

산업 현장에서 현재의 생성형 인공지능을 사용할 수 있을까? 라고 한다면,

사실 산업 현장의 인공지능 수요는 매우 많지만 데이터가 부족하다는 벽이 있다.

 

1) 보안 이슈로 인해 공개를 하지 못하거나, 2) 업종 특성상 디지털 데이터가 축적되지 못한다거나, 3) 신뢰성과 저작권으로 인해 어떤 데이터를 사용해야 할 지가 문제인 경우가 많다,

 

 

LG의 비즈니스 모델은 'Advancing AI for a better life'이다.

실제 산업 현장에서 바로 사용할 수 있는 생성 모델을 만들어 제공하는 것이다.

출처 : https://lgresearch.ai/about/vision#about1

 

명확한 비전을 수립하고 구성원들과 공유하여 실제 산업 현장에 적용 가능한 '전문가 AI'를 개발한다.

이때, 신뢰할 수 있는 고품질 데이터를 수집하여 멀티모달 이해 기술을 확보한다.

 

 

LG의 LMM (Large Multi-modal Model)은 Image-to-Text Generation에서의 Image Captioning (이미지에 대한 설명 출력)에서 더 나아가 고객의 더 많은 니즈에 맞춘 기술을 개발한다고 한다.

예컨데, '이미지 설명 및 요약', '홍보 문구 생성', '시나리오 생성', '구성 요소 추천 (음악, 화풍)', '차트 & 문서 분석' 등이 있다.

 

 

또한, 텍스트를 입력으로 넣었을 때 이미지에 대한 처리를 하는 Text-to-Image Generation에서는 색상 변화, 배경 융화 등의 작업이 이뤄진다.

타사 모델과의 차별점으로는 'Single Model'로 지원하여 고객이 어떤 모델을 써야하는지 고민하는 시간을 줄여준다는 점이다.

 

 

현재 이미지 생성 모델로 생성한 이미지를 다른 계열사와 외부 사업에서 활용 중에 있다고 한다.

출처 : https://www.lghnh.com/m/news/press/view.jsp?seq=3547&title=

 

LG 생활건강의 'Portable 타투 프린터, 임프린투'는 타투 이미지가 생성형 모델을 기반으로 생성되어 고객이 원하는 디자인에 맞게 다양한 타투를 할 수 있도록 한다.

 

 

 

이 외에도 경기도 광주의 화담숲이 있는데, '화담채 아뜰리에 미디어 아트 전시'를 생성형 AI로 만들었다고 한다.

실사의 화담숲 사진을 입력으로 (계절별) → 사진에 대한 전체적인 가이드, 스토리라인, 화풍 생성 → 이미지-텍스트 모델 프롬프트로 입력 → 제안된 컨셉에 맞춘 이미지 생성 → 사람의 손을 거쳐서 미디어 아트화

 

LG는 사용자가 바로 사용할 수 있는 생성형 AI 보다는 End-user를 계열사, 외부 사업체로 두었다고 한다.

해당 전문가들이 아이디어를 얻고 작업을 효율적으로 할 수 있도록 돕는 방향으로 진행된다.

 

 

 

Q&A에서 유익한 질문들이 많이 나와서 정리해 보았다.

 

1) zero-shot 이미지 캡셔닝에서 CIDEr 메트릭으로 평가한 이유가 있나?

: 다양한 스코어들 중 하나일 뿐임. 랭킹을 매길 때 다양한 지표로 비교를 하는데 굳이 비교를 하자면 그나마 신뢰성이 있겠다고 생각하여 사용함. (정성적 평가) CIDEr가 더 우수해서 쓴다 이런건 아님.

 

2) 일반 대중보다는 계열사를 목표로 연구하고 계신건지 연구 방향이 궁금함

: 계열사들마다 일반적인 사용을 원하는 곳도 있고 특정화된 커스텀 모델을 원하는 곳도 있음. 일반화된 모델을 만들어 두고, 그때마다 커스터마이징하여 제공하는 것을 목표로 하고 있음

 

3) 모델의 개발 과정이 궁금. 문제가 없는 이미지 데이터란 무엇인지도 궁금함

: 요새 대부분의 AI 모델이 그렇듯 엄청나게 독창적이기보다는, shutterstock이랑 계약을 해서 직접 구매한 데이터로 scratch부터 학습했음

 

4) 다양한 계열사를 가진 만큼 hallucination도 있을텐데 자체적 rule base로 관리하고 있는 것인지, 아니면 할루시네이션을 분류할 수 있는 모델을 구축해서 사용하고 있는지가 궁금함

: 할루시네이션은 LLM 연구에서 진행 중이고, 엔드 유저가 바로 일반적인 대중이 아니라 단순 아이디어를 얻어서 디자인에 활용할 수 있도록 하는, 사람 손을 한번 거치는 식으로 하고 있음

 

5) GPT Evaluation도 어떻게 보면 정성적 평가에 유사한 정량적 평가인데, 모델의 최적 성능을 평가하는 입장으로서 어떤 부분을 더 고려하고 있는지 궁금함

: 그런 evaluation을 사용하는 것은 아무래도 사람이 평가해야 하는 공수를 덜기 위해서가 크다. 뭐가 정답이고 아니다 판단하기가 애매함. 일단 사람이 전체적인 평가를 하고, GPT Evaluation을 사용하여 경향성을 비교했을 때 차이는 있었지만 경향성이 비슷하기 때문에 어느정도 대체할 수 있겠다고 판단하여 사용하게 됨

 

6) 엔드 유저가 결국은 계열사 사람들인데 그럼에도 영어로 출력되는 모델을 만든 이유는?

: 데이터가 영어 쪽이 방대해서 그렇게 학습한 것이고, 실제로 한글에서도 잘 동작을 하고 있음. 현재 영어, 한국어 지원

 

 

 

 

3. Data Intelligence Lab

수요 예측 (Demand Forecasting)에 관한 발표였는데, 개인적으로 가장 인상 깊었던 세션이었다.

 

수요 예측을 한다는 것은, 단순하게 수요를 예측한다!는 관점보다는

1) 제조 → 리테일 → 컨슈머 이 과정에서 재고를 줄이고, 생산을 효율적으로 할 수 있도록 함

2) 여러 주체가 있는 만큼 문제 해결이 어려움

 

이 두 관점이 핵심이다.

그러면서 'The Bullwhip effect (채찍효과)'에 대해서 말씀해주셨는데,

 

- 공급사슬관리에서 반복적으로 발생하는 문제점 중 하나로, 이것은 제품에 대한 수요정보가 공급사슬상의 참여 주체를 하나씩 거쳐서 전달될 때마다 계속 왜곡됨을 의미한다.
- 어떤 아이템에 대한 수요가 변동은 공급사슬상의 다른 구성원(유통업체, 제조업체, 공급업체, 2차공급업체, 3차공급업체) 각자의 입장에서 ‘만약에 대비하기에’ 충분할 정도의 재고를 축적하도록 만든다.
- 이런 대응 추세는 주문계획에서 작은 변화가 증가되고, 재고, 생산, 창고, 운송과 관련된 과도한 비용이 발생되는 가운데, 공급사슬을 통해 확산되어 나간다.

(출처 : 위키백과)

 

 

여기에서 예측해야 할 것이 맨 끝에 있는 시계열이다 보니 어려움이 많은 것이다.

 

'Sales Demanding'은 pricing, promotions, shortage가 3개의 Key로 작동한다.

이때, Inventory (재고)가 Sales (판매)를 따라가지 못 할 때 '그냥 생산 많이 하면 되지!'가 아니다. 재고 비용을 생각해야 하기 때문이다!

 

관점에 따라서 다르기 때문에 학습과 평가가 모두 어렵다.

예측 오차를 평가 지표로 쓰는 것이 맞는 지도 고민해야 한다!

 

 

 

(1) LLM based Forecasting

LG AI 연구원에서는 'LLM based Forecasting'을 하였다.

LLM의 임베딩의 이점을 살려서 단순하게 사용하였고, 모델 구조상 어디에 위치하는지에 집중하여 agent화 하였다.

수요 예측에 LLM까지 도입을 해야 하는가에 대한 고민도 있었지만, 최대한 다양하고 많은 데이터를 활용하기 위해 도입했다고 하였다.

 

예측의 큰 틀이 'Distribution Shift'를 가정하기 때문에, 이전에 있었던 일을 미래에 그대로 예측하는, 그런 단순한 문제가 아니다.

시계열을 시계열 자체 데이터만 사용해서는 예측이 불가능하다는 의미이다.

최대한 필요한 데이터를 정보화 하여 다 쓰고자 LLM을 도입하였다.

 

 

 

(2) Causality (인과성)

현재 계속 연구 시도 중인 주제라고 한다. 많은 원인들 중에서 진짜 영향을 주는 것이 맞는지? 따져보는 것이다. 

시계열을 그래프로 표현하고, 시간에 따른 변화를 그래프 수정을 통해 변화해나간다.

 

'인과성'은 정말 어려운 것이다.. 상관관계와는 다르다.

인과관계는 '반례'가 없다!

 

 

 

(3) Multi-modal Forecasting

모든 가지고 있는 데이터를 활용하기 위해서 Multi-modal 예측을 한다.

매대 사진 같은 것도 충분히 데이터가 될 수 있다. 시계열이라고 해서 시계열 데이터만 학습에 사용하는 것이 아니다.

 

 

 

(4) Probability of Forecasting

한편, 이미지와 텍스트는 사용자 측면에서 받아들이기가 쉽다. Output이 명확하기 때문이다.

하지만 예측은 사용자 입장에서 판단이 어렵다. 예측한 바를 믿고 그대로 따를 것인가에 대한 고민이 남게 되는 것이다.

 

따라서 '신뢰 구간'을 제공해야 한다. (DeepAR, Distribution Loss 참고하기)

 

 

 

연구원 분께서 마지막에 하신 말씀이 인상 깊었다.

"그래서, 예측을 잘 해야 할까요?"

예측 정확도가 1순위가 아닐 수도 있다는 말씀이셨다.

모델은 당연히 사람보다 예측 정확도가 높을 수밖에 없다. 모델은 모든 데이터를 반영할 수 있기 때문이다.

 

실제로 예측을 잘하는 분야가 늘어나고 있고, 예측 정확도가 100%라고 해서 무조건 따르는 것이 아니다.

비즈니스 임펙트가 있는지를 따져야 한다!

의사 결정에 영향을 주는 요소가 '예측 정확도'가 아닐 수 있다는 점이다. 즉, ROI 관점이 필요하고, 이는 LG 내부에서도 계속해서 고민하고 있는 과제라고 한다.

 

 

그래서 결론적으로는,

모든 형태의 데이터를 바탕으로 미리 알고 있는 정보에 대한 반영이 제대로 이뤄지면서, 변동의 속성을 파악하고 변동의 원인을 설명할 수 있는, 마지막으로 도메인 전문가와의 상호작용을 통해 더 개선되는 모델을 구축하는 것이 목표가 되겠다.

 

 


 

 

연구원 분들을 직접 뵙고 궁금한 내용을 여쭤볼 수 있는 기회를 주신 LG AI 연구원 분들께 다시 한번 감사의 말씀 드리고 싶습니다!

1. 문제 설명

유사한 기사를 묶는 기준으로 "자카드 유사도"라는 방법을 채택했다.

 

자카드 유사도는 집합 간의 유사도를 검사하는 여러 방법 중의 하나로 알려져 있는데, 두 집합 A, B 사이의 자카드 유사도 J(A, B)는 두 집합의 교집합 크기를 두 집합의 합집합 크기로 나눈 값으로 정의된다.

 

이러한 자카드 유사도를 원소의 중복을 허용하는 다중집합에 대해서 확장한다.

- 예1)

다중집합 A는 원소 1을 3개 가지고 있고 (A = {1, 1, 1}), 다중집합 B는 원소 1을 5개 가지고 있다 (B = {1, 1, 1, 1, 1}). 이 다중집합의 교집합은 원소 1을 min(3, 5)인 3개, 합집합은 원소 1을 max(3, 5)인 5개 가지게 된다.

 

- 예2)

다중집합 A = {1, 1, 2, 2, 3}, 다중집합 B = {1, 2, 2, 4, 5}라고 하면, 교집합은 {1, 2, 2}, 합집합은 {1, 1, 2, 2, 3, 4, 5}이므로 자카드 유사도는 3/7이다.

 

이를 문자열을 두 글자씩 끊어서 다중집합으로 만든 것에 응용하고자 한다.

- 예)

문자열 "France"와 "French"가 주어졌을 때, 이를 두 글자씩 끊어서 다중 집합을 만들 수 있다. 각각 {FR, RA, AN, NC, CE}, {FR, RE, EN, NC, CH}가 되며, 교집합은 {FR, NC}, 합집합은 {FR, RA, AN, NC, CE, RE, EN, CH}가 되므로 자카드 유사도는 2/8이다.

 

 

2. 입력 형식

- 다중집합 원소 사이를 비교할 때, 대문자와 소문자의 차이는 무시한다. "AB"와 "Ab", "ab"는 같은 원소로 취급한다.

 

- 입력으로 들어온 문자열은 두 글자씩 끊어서 다중집합의 원소로 만든다. 이때 영문자로 된 글자 쌍만 유효하고, 기타 공백이나 숫자, 특수 문자가 들어있는 경우는 그 글자 쌍을 버린다. 예를 들어 "ab+"가 입력으로 들어오면, "ab"만 다중집합의 원소로 삼고, "b+"는 버린다.

 

 

3. 코드

(1) Counter 사용하기

from collections import Counter

def solution(str1, str2):
	# 문자열 소문자로 변경
    str1 = str1.lower()
    str2 = str2.lower()
    
    arr1 = []
    arr2 = []
    
    # 특수문자 들어가지 않은 문자열만 취급하기
    for i in range(len(str1)-1):
        if str1[i:i+2].isalpha():
            arr1.append(str1[i:i+2])
    for i in range(len(str2)-1):
        if str2[i:i+2].isalpha():
            arr2.append(str2[i:i+2])
    
    # 중복 원소 고려를 위해 Counter 사용
    arr1_counter = Counter(arr1)
    arr2_counter = Counter(arr2)
    
    # 합집합, 교집합 구하기
    hab = list((Counter1 & Counter2).elements())
    kyo = list((Counter1 | Counter2).elements())
	
    # 집합 A, B가 모둔 공집합일 경우 J(A, B) = 1
    if len(union) == 0 and len(inter) == 0:
        return 65536
    else:
        return int(len(inter) / len(union) * 65536)

 

 

(2) 다중 집합 사용하기

def solution(str1, str2):
	# 문자열 소문자로 변경
    str1 = str1.lower()
    str2 = str2.lower()
    
    arr1 = []
    arr2 = []
    
    # 특수문자 들어가지 않은 문자열만 취급하기
    for i in range(len(str1)-1):
        if str1[i:i+2].isalpha():
            arr1.append(str1[i:i+2])
    for i in range(len(str2)-1):
        if str2[i:i+2].isalpha():
            arr2.append(str2[i:i+2])
    
    # 다중 집합 구하기
    arr1_result = arr1.copy() # 합집합
    intersection = [] # 교집합

    for i in arr2:
        if i in arr1:
            arr1.remove(i)
            intersection.append(i)
        else:
            arr1_result.append(i)
    
    if len(arr1_result) == 0 and len(intersection) == 0:
    	return 65536
    else:
    	return int(len(intersection) / len(arr1_result) * 65536)

 

 

4. 해설

(0) 공통

- 대문자와 소문자 구분이 없도록 string.lower() 함수로 문자열을 소문자로 변경해준다.

- 두 글자씩 끊어서 원소로 만들 때 영문자로 된 글자 쌍만 유효하도록 string.isalpha() 함수로 검토한다.

 

 

(1) Counter 사용하기

- 중복된 원소를 고려하기 위해서 원소의 개수를 Counter로 구한다.

- Counter는 '|', '&' 연산이 가능하다. 이때, 핵심은 Counter.elements()이다.

Counter.keys()를 하면 중복된 값이 존재해도 중복 없이 unique한 값이 반환된다. 따라서, elements()를 통해 중복된 개수만큼을 리스트로 저장한다.

 

 

(2) 다중 집합 사용하기

- 일반적인 Python의 집합은 중복 원소를 허용하지 않는다. 하지만, 다중 집합은 중복된 원소를 포함하기 때문에 실제로는 리스트에 저장하여 나타낸다.

 

- 마치 Sorting에서 stable sorting이 있고, unstable sorting이 있는 것처럼 생각하면 쉽다! 같은 '1'이지만 '1_a', '1_b'가 있는 것으로 생각하는 것이다.

 

- 다중집합에서 교집합과 합집합을 구하는 방법은 다음과 같다.

a = [1,2,2,3,4,5]
b = [1,1,2,3,4,6]

a_temp = a.copy()
a_result = a.copy()

for i in b:
	if i not in a_temp:
    		a_result.append(i)
      	else:
        	a_temp.remove(i)

a_temp는 a - b (차집합)이 되고, a_result는 합집합이 된다.

교집합은 a_result에서 a_temp 원소를 뺀 원소들이 된다.

 

 

a_temp가 필요한 이유는, a에서 remove 해버리면 원본 데이터가 손실되기 때문이다.

이 문제에서는 이후에 arr1이 필요하지 않아서 바로 arr1에서 remove를 한 것이다.

 

arr1_result = arr1.copy() # 합집합
intersection = [] # 교집합

for i in arr2:
    if i in arr1:
        arr1.remove(i)
        intersection.append(i)
    else:
        arr1_result.append(i)

 

 


reference : https://velog.io/@munang/%EA%B0%9C%EB%85%90%EC%A0%95%EB%A6%AC-%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EB%8B%A4%EC%A4%91-%EC%A7%91%ED%95%A9

1. 문제 설명

어떤 게임은 일정 피로도를 사용하여 던전을 탐험할 수 있다.

 

이때, 각 던전마다 탐험을 시작하기 위해 필요한 "최소 필요 피로도"와 던전 탐험을 마쳤을 때 소모되는 "소모 피로도"가 있다.

- 최소 필요 피로도 : 해당 던전을 탐험하기 위해 가지고 있어야 하는 최소한의 피로도

- 소모 피로도 : 던전을 탐험한 후 소모되는 피로도

 

예를 들어 '최소 필요 피로도'가 80, '소모 피로도'가 20인 던전을 탐험하기 위해서는 유저의 현재 남은 피로도는 80 이상이어야 하며, 던전을 탐험한 후에는 피로도 20이 소모된다.

 

하루에 한 번씩 탐험할 수 있는 던전이 있을 때, 한 유저가 오늘 최대한 많이 탐험하려고 할 때, 유저가 탐험할 수 있는 최대 던전 수를 반환하라.

 

 

2. 제한 사항

- k는 1 이상 5,000 이하인 자연수

- dungeons의 각 행은 각 던전의 ['최소 필요 피로도', '소모 피로도'] 이다.

 

 

3. 코드

(1) 완전 탐색 - DFS

answer = 0

def explore(power, cnt, is_visited, dungeons):
	global answer
	if cnt > answer:
    	answer = cnt
        
        for i in range(len(dungeons)):
            if power >= dungeons[i][0] and is_visited[i] == False:
                is_visited[i] = True
                explore(power - dungeons[i][1], cnt + 1, is_visited, dungeons)
                is_visited[i] = False

def solution(k, dungeons):
	global answer
        is_visited = [ False for _ in range(len(dungeons)) ]

        explore(k, 0, is_visited, dungeons)

        return answer

 

 

(2) 순열

from itertools import permutations

def solution(k, dungeons):
	answer = 0
    
        for pemutation in permutations(dungeons, len(dungeons)):
            power = k
            cnt = 0

            for need, spend in permutation:
                if power >= need:
                    power -= spend
                    cnt += 1
                else:
                    break

            answer = max(answer, cnt)

        return answer

 

 

4. 해설

(1) 완전 탐색 - DFS

깊이 우선 탐색 방식으로 모든 경우를 따져준다.

전역 변수 answer를 선언하여, 던전의 최대 탐험 개수를 갱신했을 때 answer 값을 바꿔준다.

 

이미 지나온 던전인지를 저장하는 is_visited 배열을 만들어주고 DFS를 수행하는 함수인 explore를 호출한다.

 

explore 함수는

- 전역 변수 answer보다 탐험한 던전의 개수가 더 많을 경우 갱신을 해준다.

- 던전의 경우를 for문으로 모두 검토하면서 방문하지 않았는지, 방문할 수 있는지 (최소 필요 피로도를 만족하는지) 확인하여 재귀적 호출을 진행한다.

- 핵심은 호출 이후로 is_visited[i] = False로 만들어주어, 다음 경우에는 다시 방문할 수 있도록 하는 것이다.

 

 

(2) 순열

from itertools import permutations를 통해서 가능한 모든 경우의 수를 구한다.

 

- for permutation in permutations(dungeons, len(dungeons))

예시 :

([80, 20], [50, 40], [30, 10])
([80, 20], [30, 10], [50, 40])
([50, 40], [80, 20], [30, 10])
([50, 40], [30, 10], [80, 20])
([30, 10], [80, 20], [50, 40])
([30, 10], [50, 40], [80, 20]))

 

1. 문제 설명

셀 수 있는 수량의 순서있는 열거 또는 어떤 순서를 따르는 요소들의 모음을 튜플 (tuple)이라고 한다. n개의 요소를 가진 튜플을 n-tuple이라고 한다.

 

튜플은 다음과 같은 성질을 가지고 있다.

- 중복된 원소가 있을 수 있다. (예. (2, 3, 1, 2))

- 원소에 정해진 순서가 있으며, 원소의 순서가 다르면 서로 다른 튜플이다. (예. (1, 2, 3) ≠ (1, 3, 2))

- 튜플의 원소 개수는 유한하다.

 

(a1, a2, a3, ..., an)이 주어질 때, 집합 기호를 통해 다음과 같이 나타낼 수 있다.

- {{a1}, {a1, a2}, {a1, a2, a3}, {a1, a2, a3, a4}, ... {a1, a2, a3, a4, ... , an}

 

예를 들어 튜플이 (2, 1, 3, 4)인 경우 이는

- {{2}, {2, 1}, {2, 1, 3}, {2, 1, 3, 4}}

- {{2, 1, 3, 4}, {2}, {2, 1, 3}, {2, 1}}

- {{1, 2, 3}, {2, 1}, {1, 2, 4, 3}, {2}}

이렇게 다양하게 표현이 가능하다.

 

특정 튜플을 표현하는 집합이 담긴 문자열 s가 매개변수로 주어질 때, s가 표현하는 튜플을 배열에 담아 반환하도록 하라.

 

 

2. 제한 사항

- s의 길이는 5 이상 1,000,000 이하이다.

- s는 숫자와 '{', '}', ','로만 이루어져 있다.

- 숫자가 0으로 시작하는 경우는 없다.

 

 

3. 코드

def solution(s):
    s1 = s.lstrip('}').rstrip('{').split('},{')
    s2 = []
    
    # 각 원소로 이루어진 배열을 삽입
    for i in s1:
        if '{{' in i:
            i = i.lstrip('{{')
        if '}}' in i:
            i = i.rstrip('}}')
            
        s2.append(i.split(','))
    
    # 원소의 길이를 기준으로 배열 정렬
    s3 = []
    for tuple in sorted(s2, key=len):
        for t in tuple:
        	
            # 배열에 존재하지 않는 경우 순서대로 추가
            if t not in s3:
                s3.append(t)
    
    return list(map(int, s3))

 

 

4. 해설

집합 형태의 s가 문자열로 들어왔고, 이를 원소 하나하나씩 떼어주는 작업이 필요하다.

- 문자열 맨 앞의 { 와 맨 뒤의 }를 제거해준다.

(예시 결과 : {2}, {2, 1}, {2, 1, 3}, {2, 1, 3, 4})

 

- '},{'를 기준으로 split 해준다.

(예시 결과 : ['{{2', '2, 1', '2, 1, 3', '2, 1, 3, 4}}'])

 

 

떼어낸 문자열을 원소 하나씩으로 분리해준다.

- 원소에 {{ 가 있거나 }} 가 있는 경우, 제거해준다.

(예시 결과 : ['2', '2, 1', '2, 1, 3', '2, 1, 3, 4']

 

- ','를 기준으로 split 해준다.

(예시 결과 : [['2'], ['2', '1'], ['2', '1', '3'], ['2', '1', '3', '4']]

 

 

각 배열 요소들을 짧은 길이 순서대로 sorting 해주고, 정답 배열에 존재하지 않는 경우에 원소를 추가한다.

- sorted(s2, key=len) : key에 정렬 기준 함수를 적어주면 되는데, python 내장 함수인 len을 적어주었다.

1. 문제 설명

지도에서 도시 이름을 검색하면 해당 도시와 관련된 맛집 게시물들을 데이터베이스에서 읽어 보여주는 서비스를 개발하고 있다.

이때, 데이터베이스에 캐시를 적용하여 성능 개선을 시도하고 있지만, 캐시 크기를 얼마로 해야 효율적인지 몰라 난감한 상황이다.

DB 캐시를 적용할 때 캐시 크기에 따른 실행시간 측정 프로그램을 작성하라.

 

 

2. 입력 형식

- 캐시 크기 (cacheSize)와 도시이름 배열 (cities)을 입력받는다.

- cacheSize는 수이며, 범위는 0  ≦ cacheSize ≦ 30 이다.

- 각 도시 이름은 공백, 숫자, 특수문자 등이 없는 영문자로 구성되며, 대소문자 구분을 하지 않는다.

 

 

3. 출력 형식

- 입력된 도시 이름 배열을 순서대로 처리할 때, '총 실행시간'을 출력한다.

 

 

4. 조건

- 캐시 교체 알고리즘은 LRU (Least Recently Used)를 사용한다.

- Cache hit일 경우 실행시간은 1이다.

- Cache miss일 경우 실행시간은 5이다.

 

 

5. 코드

def solution(cacheSize, cities):
	# 대소문자 구별하지 않도록 소문자로 만들어주기
    cities = [c.lower() for c in cities]
    
    # 캐시 사이즈가 0인 경우 전부 Cache miss로 간주
    if cacheSize == 0:
        return len(cities) * 5
    
    # { 도시 이름 : 참조 시간 } 저장할 딕셔너리 선언
    cache = dict()
    total = 0
	
    
    for c in cities:
        if len(cache) < cacheSize:
            if c not in cache.keys():
                total += 5
            else:
                total += 1
        else:
            if c not in cache.keys():
                total += 5
                
                # 가장 value가 큰 요소를 제거
                max_time = -1
                max_key = c
                for city, time in cache.items():
                    if max_time < time:
                        max_key = city
                        max_time = time

                del cache[max_key]

            else:
                total += 1
        
        # 공통적으로 c 아닌 도시들에 +1, c 도시에 0 부여
        cache = { city : time+1 for city, time in cache.items()}
        cache[c] = 0
        
    return total

 

 

6. 해설

https://tech.kakao.com/posts/344

 

카카오 신입 공채 1차 코딩 테스트 문제 해설 - tech.kakao.com

‘블라인드’ 전형으로 실시되어 시작부터 엄청난 화제를 몰고 온 카카오 개발 신입 ...

tech.kakao.com

카카오 기술 블로그에 나와있는 예시 설명이다.

 

놓칠 수 있는 포인트가

1. 대소문자 구별하지 않는다는 점 : lower case로 다 변환해주기

2. cacheSize로 0이 들어올 수 있다는 점 : 전부 cache miss로 간주하여 도시 개수 * 5 해주기

3. 단순히 가장 예전에 cache에 들어왔다고 해서 cache에서 제거해야 할 대상은 아니라는 점 : '참조 시간'의 문제임

 

 

캐시에 cacheSize만큼 저장되지 않은 경우,

중복 체크만 해주고 이미 있는 도시에 대해 value + 1, 새로 들어온 도시에 대해 0으로 만들어 준다.

 

캐시에 cacheSize만큼 저장되어 있는 경우에는,

가장 참조 시간이 긴 도시를 제거하고 새로 들어온 도시를 참조시간 0으로 하여 추가해준다. 역시 이미 있는 도시에 대해서는 value + 1을 해준다.

1. 문제 설명

코니는 매일 다른 옷을 조합하여 입는데, 각 종류별로 최대 1가지 의상만 착용할 수 있다.

예를 들어, 위의 경우 동그란 안경과 검정 선글라스 중 하나를 착용할 수 있지만 둘 다 착용할 수는 없다.

코니는 하루에 최소 한 개의 의상은 입는다.

 

코니가 가진 의상들이 담긴 2차원 배열이 주어질 때 서로 다른 옷의 조합의 수를 반환한다.

 

 

2. 제한 조건

- 2차원 배열은 [의상의 이름, 의상의 종류]로 이루어져 있다.

- 같은 이름을 가진 의상은 존재하지 않는다.

 

 

3. 코드

from collections import Counter

def solution(clothes):
    closet = Counter([sort for name, sort in clothes])
    
    answer = 1
    for sort, num in closet.items():
        answer *= (num + 1)
    
    return answer - 1

 

 

 

4. 해설

from collections import Counter는 매우 유용한 함수이다. 리스트에서 종류와 개수에 대해 dictionary를 만들 때 사용하면 좋다.

이 문제에서 특별하게 사용된 것은 [ sort for name, sort in clothes ] 이다.

sort를 기준으로 개수를 구해주도록 하여, sort에 대한 중복 확인, 개수 체크를 할 필요가 없어서 유용하다.

 

각 sort 별로 하나씩 입거나 입지 않은 경우를 고려하여 num + 1을 해주었다.

그리고 그 경우의 수를 다 곱하여 모든 경우의 수를 구하는데, 이때 정답은 -1을 해주어야 한다.

아무것도 입지 않은 경우는 제외해주어야 하기 때문이다.

+ Recent posts