SQL은 뇌빼고 풀어도 다 풀 수 있는데 여기 문제는 코드짜기 전 어떻게 풀어야하는지 깊게 생각해야하는것같다. (나만 그럴수도)
복습할 문제
1. 3번 연속하는 등장하는 숫자 찾기 (180번)
Find all numbers that appear at least three times consecutively.
Return the result table inany order.
The result format is in the following example.
-- num이 같은 숫자로 연속 3번이상 등장할 경우 COUNT
-- n번째 id의 num과, n+1.., n+2의..num이 모두 같을 경우 COUNT -> WHERE?
-- 힌트: 조인 사용
SELECT DISTINCT A.num AS ConsecutiveNums
FROM Logs A
JOIN Logs B
ON B.id = A.id + 1
JOIN Logs C
ON C.id = A.id + 2
WHERE A.num = B.num and B.num = C.num
# 어떠한 숫자가 3번 이상 등장하는지 -> JOIN 2번, DISTINCT 사용
2. 각 부서 별로 최고의 연봉 갖는 사람의 이름 추출 (184번)
오답)
-- 부서별 MAX연봉 직원 찾기 ,any order
SELECT B.name AS Department, A.name AS Employee, MAX(A.salary) AS Salary
FROM Employee A
JOIN Department B
ON A.departmentId = B.id -- 직원이름이 어떠한 부서 아래 있는지 참조
GROUP BY B.name
문제 : 추출되는 A.name이 최고 연봉을 가지는 사람이 아님. (랜덤임)
만약 문제가 각 부서 별 최고 연봉금액이면 맞지만, 연봉이 얼마인지 + 그사람의 이름을 가져오려면 서브쿼리 써야함.
# 부서별 MAX연봉 직원 찾기 ,any order
SELECT B.name AS Department,
A.name AS Employee,
MAX(A.salary) AS Salary
FROM Employee A
JOIN Department B
ON A.DepartmentId = B.id
WHERE (A.departmentId, A.salary) IN -- 두개 이상을 비교하는 것이기 때문에 괄호써줘야함.
(
SELECT departmentId, MAX(salary) A.departmentId아님 -> 서브쿼리라서 테이블 내 컬럼명 그대로
FROM Employee
GROUP BY departmentId
)
서브쿼리를 쓴 이유 - 부서별로 가장 높은 salary를 미리 계산해서 메인쿼리와 비교해야 하기 때문입니다.
IN을 쓴 이유 - (departmentId, max salary) 조합 중 하나에 해당하는 직원들을 찾기 때문입니다.
1. 문자열 이어 붙이기 -> CONCAT()-> 주의: ROUND/AVG 등 함수 내부가 아닌 제일 밖에 사용!
예시)
SELECT ROUTE,
CONCAT(ROUND(SUM(D_BETWEEN_DIST),2), 'km') AS TOTAL_DISTANCE,
CONCAT(ROUND(AVG(D_BETWEEN_DIST),3), 'km') AS AVERAGE_DISTANCE
FROM SUBWAY_DISTANCE
Q: 자 CTE 들어가기전, 나는 정말 자주 나오고, 리트코드 + 회사 입사 코딩테스트 (SQL 데이터분석)에 맞게 문제를 풀어보고 싶어. 도와줄래?
A:
문제 1: 부서별 평균보다 높은 연봉 가진 직원 찾기 (난이도:⭐)
각 부서에서, 해당 부서의 평균 연봉보다 높은 연봉을 받는 직원의 이름과 연봉을 출력하라.
테이블: employees(department, name, salary)
# 각 부서에서, 해당 부서의 평균 연봉보다 높은 연봉을 받는 직원의 이름과 연봉을 출력하라.
# 테이블: employees(department, name, salary)
SELECT name,salary,department FROM employees e
WHERE salary >
(SELECT AVG(salary)
FROM employees
WHERE department = e.department);
# 핵심 -> 부서 평균 연봉을 구하고, 그걸 직원 연봉과 비교해야함
# (상관)서브쿼리 써야함
🔥 문제 2: 매니저가 3명 이상 직원 관리하는 경우만 출력 (난이도:⭐⭐)
직원 수가 3명 이상인 매니저의 이름과 직원 수를 출력하라.
테이블: employees(employeeId, name, managerId)
# 문제 2: 매니저가 3명 이상 직원 관리하는 경우만 출력 (난이도:⭐⭐)
# 직원 수가 3명 이상인 매니저의 이름과 직원 수를 출력하라.
# 테이블: employees(employeeId, name, managerId)
SELECT m.name AS manger_name, COUNT(e.employeeId) AS ct
FROM employees e
INNER JOIN employees m
ON e.managerId = m.employeeId
GROUP BY m.name
HAVING COUNT(e.employeeId) >= 3;
# 1.JOIN + CASE WHEN 해서 직원 담당 매니저 변수만들기
# 2.COUNT활용해서 COUNT > 3인 매니저 이름과 직원 수 출력하기 -> GROUPBY + HAVING으로!
처음엔 CASE WHEN을 썼더니 반응이,..
그냥 욕을 해.
이걸 알아야 풀 수 있다고.!
🔥 문제 3: 이전 연도보다 연봉이 오른 직원만 출력 (난이도:⭐⭐⭐)
같은 직원이 여러 해에 걸쳐 기록된 salaries 테이블에서, 이전 연도보다 연봉이 증가한 경우만 employeeId, year, salary를 출력하라.
# 직원과 매니저 이름을 같이 출력하라. (Self Join 구조 익히기)
# Employee(employeeId, name, salary, managerId)
# → managerId가 employeeId를 참조하고 있음
SELECT e.name, m.name
FROM employees e
JOIN employees m
ON e.employeeId = m.managerId
각 직원이 자기 매니저보다 연봉이 높으면 ‘Higher’, 낮으면 ‘Lower’라고 표시하라.
컬럼: employee_name, manager_name, 비교결과
# 각 직원이 자기 매니저보다 연봉이 높으면 ‘Higher’, 낮으면 ‘Lower’라고 표시하라.
# 컬럼: employee_name, manager_name, 비교결과
SELECT e.name AS employee_name, m.name AS manager_name, e.salary,
CASE
WHEN e.salary > m.salary THEN 'Higher'
ELSE 'Lower'
END AS '비교결과'
FROM employees e
JOIN employees m
ON e.managerId= m.employeeId;
문제 3.
매니저가 없는 직원은 'No Manager'로 표기하라.
CASE WHEN을 IS NULL 조건과 함께 써봐
참고) 테이블은 아래와 같음.
# 매니저가 없는 직원은 ‘No Manager’로 표시
# employeeId/managerId/name/salary/department
SELECT e.name AS employee_name,
CASE
WHEN m.name IS NULL THEN 'No Manager'
ELSE m.name
END AS "매니저"
FROM employees e
JOIN employees m
ON e.managerId = m.employeeId;
문제 4.
직원이 자기 매니저보다 1.1배 이상 높은 연봉이면 ‘Fast Climber’, 동일하면 ‘Peer’, 아니면 ‘Underpaid’ 라고 표시
# 직원이 자기 매니저보다 1.1배 이상 높은 연봉이면 ‘Fast Climber’,
# 동일하면 ‘Peer’, 아니면 ‘Underpaid’ 라고 표시
# columns - 'employeeId/name/salary/managerId'
SELECT name,
CASE
WHEN e.salary > ( 1.1 * m.salary) THEN "Fast Climber"
WHEN e.salary = m.salary THEN "Peer"
ELSE "Underpaid"
END AS "연봉 상황"
FROM employees e
INNER JOIN employees m
ON e.managerId = m.employeeId;
궁금한 점
어떻게 e.salary m.salary를 각각 직원 연봉과 매니저 연봉으로 구분할 수 있는가?
-> 먼교수님 말씀...
요새 GPT는 노트북 카메라로 얼굴 보면서 대답하나보다. 괴물처럼 생겨서 화는 나지 않는다.
결국, INNER JOIN , ON -> 부터 관계가 생기면서 (두 테이블 연결하면서) 인지함.
문제 5.
매니저 이름이 ‘Kim’인 사람만 필터해서, 그들과 연봉 비교 결과 출력하기
예: Kim보다 높으면 'Surpassed Kim', 낮으면 'Still Climbing' 등
# 매니저 이름이 ‘Kim’인 사람만 필터해서, 그들과 연봉 비교 결과 출력하기
# 예: Kim보다 높으면 'Surpassed Kim', 낮으면 'Still Climbing' 등
# table = 'employeeId','name','salary','managerId'
SELECT e.name AS employee_name,
e.salary AS employee_salary,
m.name AS manager_name,
m.salary AS manager_salary,
CASE
WHEN e.salary > m.salary THEN "Surpassed Kim"
ELSE 'Still Climbing'
END AS "비교 결과"
FROM employees e
INNER JOIN employees m
ON e.managerId = m.employeeId
WHERE m.name = "Kim";
문제 6.
부서가 같은 직원들 중 매니저보다 연봉이 낮은 사람만 출력, 그리고 'Raise Suggested' 라는 컬럼 추가
# 부서가 같은 직원들 중 매니저보다 연봉이 낮은 사람만 출력하고 그 사람들에게 "Raise Suggested"라는 데이터 채우기, column명은 "평과 결과"
SELECT e.name AS employee_name,
e.salary AS employee_salary,
m.name AS manager_name,
m.salary AS manager_salary,
CASE
WHEN e.salary < m.salary THEN "Raise Suggested"
END AS "평가 결과"
FROM employees e
INNER JOIN employees m
ON e.managerId = m.employeeId
WHERE (e.salary < m.salary) and (e.department = m.department);
문제 7.
각 매니저별로, 자기보다 연봉 높은 직원을 몇 명이나 두고 있는지 출력하라
Self Join + GROUP BY + COUNT + CASE WHEN 조합해야 함
# 각 매니저별로, 자기보다 연봉 높은 직원을 몇 명이나 두고 있는지 출력하라
# Self Join + GROUP BY + COUNT + CASE WHEN 조합해야 함
SELECT m.name AS manager_name,
COUNT(CASE WHEN m.salary < e.salary THEN 1 END) AS "better than me"
FROM employees e
INNER JOIN employees m
ON e.managerId = m.employeeId
GROUP BY m.name;
문제 8.
직원이 속한 부서에 ‘김 매니저’가 있으면 ‘Under Kim’, 없으면 ‘Other Manager’ 라고 표시하라.
Self Join + CASE WHEN + 부서 조건 조합
# 직원이 속한 부서에 ‘김 매니저’가 있으면 ‘Under Kim’, 없으면 ‘Other Manager’ 라고 표시하라.
# table = 'employeeId','name','salary','managerId','department'
SELECT department, e.name AS employee_name,
CASE
WHEN m.name = "Kim" THEN "Under Kim"
ELSE "Other Manager"
END AS "김 매니저 유무"
FROM employees e
INNER JOIN employees m
ON e.managerId = m.employeeId
WHERE e.department = m.department
문제 9.
각 직원이 '매니저와 연봉 차이'가 20% 이상이면 Alert, 아니면 Normal, 매니저 없으면 Unknown 출력
# 각 직원이 '매니저와 연봉 차이'가 20% 이상이면 Alert,
# 아니면 Normal, 매니저 없으면 Unknown 출력
SELECT e.name AS employee_name,
e.salary AS employee_salary,
m.name AS manager_name,
m.salary AS manager_salary,
CASE
WHEN e.managerId IS NULL THEN "Unknown"
WHEN ABS(e.salary - m.salary) > (e.salary) * 0.8 THEN "Alert"
ELSE "Normal"
END AS "매니저 연봉 차이"
FROM employees e
LEFT JOIN employees m
ON e.managerId = m.employeeId;
SELECT position, AVG(salary) FROM employee
GROUPBY position;
WHERE - 행 필터링 (데이터를 가져오기 전에 필터링함. 즉, GROUP BY 전에 적용됨)
GROUPBY- 집계함수 (행들을 그룹으로 묶는 애”, 동일한 값을 기준으로 행들을 묶어서 통계용으로 사용함.)
HAVING - “GROUP BY로 묶은 결과에 조건 거는 애” WHERE은 GROUP BY 이전, HAVING은 그 이후!
"연봉 5만 이상인 사람들만 보여줘"
SELECT name,salary FROM employee
WHERE salary > 50000
"부서별 평균 연봉 구해줘"
SELECT AVG(salary),department FROM employee
GROUPBY department;
"부서별 평균 연봉이 6만 넘는 부서만 보여줘"
SELECT AVG(salary),department FROM employee
GROUPBY department
HAVING AVG(salary) > 60000;
#GPT가 내준 문제...
풀어보자
-- 문제: 부서별 평균 월급을 구하되, -- 개별 직원 중에서 월급이 40000 이하인 직원은 제외하고, -- 평균이 70000 이상인 부서만 보여줘라.
-- 문제: 부서별 평균 월급을 구하되,
-- 개별 직원 중에서 월급이 40000 이하인 직원은 제외하고,
-- 평균이 70000 이상인 부서만 보여줘라.
SELECT department, AVG(salary) FROM Employee
WHERE salary > 40000
GROUP BY department
HAVING AVG(salary) > 70000;
정답!
다음문제를 줬다..
각 부서별로 월급 40000 넘는 직원 수를 구하고, 그 수가 2명 이상인 부서만 보여줘라”
월급 40000 초과인 직원들만 대상으로, 부서별 평균 연봉과 인원 수 출력. 단, 인원이 2명 이상인 부서만."
SELECT AVG(salary), COUNT(name) FROM Employee
WHERE salary > 40000
GROUP BY department
HAVING COUNT(name) >= 2;
아 맞추니까 계속 내내 이쉨..
SELECT AVG(salary), COUNT(name) FROM Employee
WHERE salary > 40000
GROUP BY department
HAVING COUNT(name) >= 2;
또 맞추니까 어려운 문제를 내보겠단다.
SELECT name, salary, department FROM employee
GROUP BY department
HAVING MAX(salary);
모르겠어ㅠ
ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㄱ창의적이네...
머리 꽁꽁 싸메고 해보자..
"각 부서에서 가장 높은 연봉을 받는 직원 출력"
SELECT department,employeeId, salary FROM employee
GROUP BY department
HAVING MAX(salary);
ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ하지만 김피탕은 맛있는걸요...
배우고 갑니다.. 죄송합니다...
다시 해볼게요.!
"각 부서에서 가장 높은 연봉을 받는 직원 출력"
서브 쿼리 사용
SELECT department, employeeId, name, salary FROM employee e
WHERE salary = (
SELECT MAX(salary)
FROM employee
WHERE department = e.department #이건 바깥 쿼리에서 선택된 직원(e)의 department 값.
#서브쿼리에서는 "이 직원이 속한 부서에서, 최고 연봉이 얼마냐?" 를 구해야 하니까 이게 필요함.
);
이제 서브쿼리 관련해서 지옥 3문제 드가자~
# 문제 1. 자기 부서 평균보다 더 많이 받는 직원 찾기
# "각 직원 중, 본인이 속한 부서의 평균 연봉보다 더 높은 급여를 받는 사람을 찾아라."
SELECT name,department,salary,employeeId FROM employee e
WHERE salary = (
SELECT AVG(SALARY)
FROM employee
WHERE department = e.department);
문제 1정답
# 문제 2. 각 직원보다 연봉 높은 사람 수 세기
# "모든 직원에 대해, 자기보다 연봉 높은 사람이 몇 명인지를 구해라."
SELECT employeeId,salary FROM employee e
WHERE salary = (
SELECT COUNT(employeeId)
FROM employee
WHERE salary > e.salary);
문제 2 오답!!!
정답은 아래와 같다.
SELECT employeeId, name, salary,
(
SELECT COUNT(*)
FROM employee e2
WHERE e2.salary > e1.salary
) AS higher_salary_count
FROM employee e1;
# 문제 3. 자기 부서의 최고 연봉자인 직원 찾기
# "자기 부서에서 가장 높은 급여를 받는 직원을 찾아라."
SELECT name, department, salary FROM employee e
WHERE salary = (
SELECT MAX(salary)
FROM employee
WHERE department = e.department
);
어텐션 메커니즘이란, 트랜스포머의 기반이 되는 모델이자, seq2seq의 단점을 보완할 수 있는 개념이다.
멀수록 잊혀진다(decoder) --- > 갈수록 흐려지는 정보에 ATTNETION하자!!
seq2seq 모델은 기존 RNN과 같이 고정된 길이의 벡터를 입력으로 받아 고정된 길이의 벡터를 출력하는 구조를 갖고 있음
이 구조는 시퀀스 길이가 길어질수록 정보 손실이 발생함!
이러한 한계를 보완하기 위해 Attention Mechanism이 도입되었다.
어텐션 메커니즘은 입력 문장의 모든 단어를 동일한 가중치로 취급하지 않고, 출력 문장에서 특정 위치에 대응하는 입력 단어들에 더 많은 가중치를 부여하는 원리로 만들어졌다.
이를 통해 입/출력의 길이가 달라도 모델이 상대적으로 더 정확하고 유연하다고 볼 수 있다.
seq2seq
인코더: 입력 시퀀스를 context vector라는 하나의 고정된 크기의 벡터 표현으로 압축
디코더: 이 context vector를 통해서 출력 시퀀스를 만들어냄
문제점: 1 - 하나의 고정된 크기의 벡터에 모든 정보를 압축하려고 하다보니 정보 손실 발생!
2 - RNN의 고질적인 문제인 기울기 소실이 존재!\
1. 어텐션의 기본 아이디어
어텐션의 기본 아이디어는 디코더에서 출력 단어를 예측하는 매 시점(time step)마다, 인코더에서의 전체 입력 문장을 다시 한 번 참고한다는 점이다.
단, 전체 입력 문장을 전부 다 동일한 비율로 참고하는 것이 아니라, 해당 시점에서 예측해야할 단어와 연관이 있는 입력 단어 부분을 좀 더 집중(attention)해서 보는 것이다.
중요하니까 자세히..!!
디코더(Decoder)에서출력 단어를예측하는매 시점(time step)마다인코더(Encoder)에서의 전체 입력 문장(sequence)을다시 한 번 참고한다는 것..
단,전체 입력 sequence를참고하는게 아니라해당 시점에서 예측해야할단어와연관이 있는 입력 단어부분을 좀 더집중해서 보겠다는 것이다.
2. 어텐션 함수(Attention Function)
파이썬의 딕셔너리 함수를 떠올려보자. key-value, 쌍으로 이루어진 자료형이다.
이 딕셔너리 자료형은 키를 통해서 매핑된 값을 바로 찾아낼 수 있다는 특징을 갖고 있다.
dict = {"2017" : "Transformer", "2018" : "BERT"}의 경우, 2017은 키, Transformer은 값(value)에 해당한다.
이 개념을 통해서 어텐션 함수에 대해서 그림을 통해 알아보자.
어텐션을 함수로 표현하면 주로 다음과 같이 표현할 수 있다. Attention(Q, K, V) = Attention Value
어텐션 함수는 주어진 '쿼리(Query)'에 대해서 모든 '키(Key)'와의 ★ 유사도를 각각 구한다. ★
그리고 구해낸 이 유사도를 키와 매핑되어있는 각각의 값에 반영하고 유사도가 반영된 값을 모두 더해서 리턴.
여기서는 이를 어텐션 값(Attention Value)이라고 하고, 지금부터 배우게 되는 seq2seq + 어텐션 모델에서 Q, K, V에 해당되는 각각의 Query, Keys, Values는 각각 다음과 같다.
Q = Query : t 시점의 디코더 셀에서의 은닉 상태
K = Keys : 모든 시점의 인코더 셀의 은닉 상태들
V = Values : 모든 시점의 인코더 셀의 은닉 상태들
Attention모델은인코더 층은seq2seq층과동일하지만디코더 층에서차이가 남.
디코더 층에서는 RNN계열 층을 지나고Attention 층을 지나는데 이때 Attention층에서는시점마다RNN계열층의은닉상태(Quary)와인코더에서의전체 입력 시퀀스 정보(Key,Value)를다시 한번참고한다.
✅ 쿼리 (Query) • 쿼리는 어텐션 메커니즘에서 주목할 대상을 나타내는 정보 • 쿼리는 보통 출력 시퀀스의 현재 위치 또는 상태와 관련이 있으며, 어텐션 메커니즘을 통해 중요한 입력 요소를 찾는 데 사용 • 기계 번역에서 디코더의 현재 출력 단어에 해당하는 쿼리가 사용될 수 있으며, 이 쿼리는 입력 문장의 어떤 부분에 더 집중해야 하는지를 결정
✅ 키 (Key) • 키는 입력 시퀀스의 각 요소에 대한 정보를 나타냄 • 키는 쿼리와 비교되어 유사성을 측정하며, 어떤 입력 요소가 현재 쿼리와 얼마나 관련 있는지를 결정하는 데 사용. • 키는 일반적으로 입력 시퀀스의 각 요소 (예: 단어 또는 문장)에 대한 벡터 또는 표현으로 표현됨
✅ 값 (Value) • 값은 입력 시퀀스의 각 요소에 대한 정보를 담고 있다. • 값은 어텐션 메커니즘에서 가중 평균을 계산할 때 사용되며, 쿼리와 키의 유사성에 따라 값에 가중치가 부여된다. • 값은 주로 입력 시퀀스의 각 요소에 대한 정보를 포함하는 벡터 또는 표현으로 표현된다.
이러한Q,K,V(쿼리,키,벨류)를 가지고Attention층에서는Attention 함수가사용되는데 이때 다양한 함수(Attention Score Function)를이용해서Attention Score가 만들어지고 보통 SoftMax 함수를 통해Attention Value가 생성됨
어텐션 함수는 주어진 쿼리(Query)에 대해모든 키(Key)와의 유사도를 구한다.
벡터간의 거리(유사도)를 구하는 방법은내적, 코사인 유사도, 유클리디안 거리방법 등이 있지만내적 유사도를 보통 사용한다고 한다.
유사도 개념 중요 ★
여기서 Attention Score 함수 중 Dot-product Attention에 대해서 알아보자!!
아래 그림 참조
인코더의 시점(time step)을 각각 1, 2, ... N이라고 하였을 때 인코더의 은닉 상태(hidden state)를 각각ℎ1,ℎ2, ...ℎ𝑁라고 합시다. 디코더의 현재 시점(time step) t에서의 디코더의 은닉 상태(hidden state)를𝑠𝑡라고 해보자.
어텐션 메커니즘의 첫 걸음인 어텐션 스코어(Attention score)에 대해서 배우기전에, 이전 챕터 배웠던 디코더의 현재 시점 t에서 필요한 입력값을 다시 떠올려보자. 시점 t에서 출력 단어를 예측하기 위해서 디코더의 셀은 두 개의 입력값을 필요로 하는데, 바로 이전 시점인 t-1의 은닉 상태와 이전 시점 t-1에 나온 출력 단어다.
그런데 어텐션 메커니즘에서는 출력 단어 예측에 또 다른 값을 필요로 하는데 바로 어텐션 값(Attention Value)이라는 새로운 값이다. t번째 단어를 예측하기 위한 어텐션 값을𝑎𝑡이라고 해보자.
어텐션 스코어란 현재 디코더의 시점 t에서 단어를 예측하기 위해, 인코더의 모든 은닉 상태 각각이 디코더의 현 시점의 은닉 상태𝑠𝑡와 얼마나 유사한지를 판단하는 스코어값이다.
닷-프로덕트 어텐션에서는 이 스코어 값을 구하기 위해𝑠𝑡를 전치(transpose)하고 각 은닉 상태와 내적(dot product)을 수행합니다. 즉, 모든 어텐션 스코어 값은 스칼라입니다. 예를 들어𝑠𝑡과 인코더의 i번째 은닉 상태의 어텐션 스코어의 계산 방법은 아래와 같습니다.
이번에 살펴본 RNN 구조는 두 개로 나누어, 하나는 인코더, 하나는 디코더로 명명한 후, 두 개의 RNN을 연결해서 사용하는 인코더-디코더 구조이다.
해당 인코더-디코더 구조는 "입력 문장"과 "출력 문장"의 길이가 다를 경우에 주로 사용한다.
대표적인 예시로 번역기/텍스트 요약이 있다.
(영어 -> 한국어로 번역 시 문장의 길이가 달라질 수 있으므로!
(원본 텍스트 -> 요약 -> 문장 수 감소 {문장 길이 변화})
1. 시퀀스-투-시퀀스 (Sequence-to-Sequence, seq2seq)
시퀀스-투-시퀀스는 입력된 시퀀스로부터 다른 도메인의 시퀀스를 출력하는 다양한 분야에서 사용된다고 한다. (챗봇, 기계 번역등)
입력 시퀀스와 출력 시퀀스를 각각 질문&대답으로 구성하면 챗봇을 만들 수 있고, 각각 입력 문장&번역 문장으로 만들면
번역기로 만들 수 있는 것이다. 그 외에도 텍스트 요약이니ㅏ STT(speech to text)에서도 쓰일 수 있다고 함.
seq2seq는 번역기에서 대표적으로 사용되는 모델로, 딥 러닝의 '블랙 박스'에서 확대해가는 방식으로 설명한다.
이제 위 그림에서 i am a student에서 프랑스어로 변화하는 내부 과정을 살펴보자.
인코더와 디코더를 통해 변환되는데, 여기서 중요한 포인트가 있다.
인코더는 입력 문장의 모든 단어들을 순차적으로 입력받은 뒤에 마지막에 모든 단어 정보를 압축해서 하나의 벡터로 만드는데, 이를 컨텍스트 벡터라고 한다.
(입력 문장의 정보가 하나의 context vector로 모두 압축 -> 인코더는 context vector를 디코더로 전송! -> 디코더는 입력받은 context vector를 하나씩 순차적으로 출력하는 과정)
실제로 사용되는 seq2seq 모델에서는 context vector가 수백개의 차원을 갖고 있다고 한다. (그림에서는 4차원)
또한, 성능 이슈로 인해 실제로는 바닐라 RNN이 아닌, LSTM/GRU 셀로 구성한다고 함!
주황색 인코더를 자세히 보면, 입력 문장은 단어 토큰화를 통해서 단어 단위로 쪼개어지고, 단어 토큰 각각은 RNN 셀의 각 시점에서 입력이 된다. 인코더 LSTM 셀은 모든 단어를 입력받은 뒤 인코더 LSTM 셀의 마지막 시점의 은닉 상태를 디코더 RNN 셀로 넘겨주는데, 이게 contexet vector인 것이다! 따라서 context vector는 디코더 RNN 셀의 첫 번쨰 은닉 상태겠지?
이제 그림의 디코더의 LSTM 셀을 보면, <sos>라는 심볼을 볼 수 있다.
sos는 초기 입력으로 문장의 시작을 의미하는 심볼이라고 하고, 이게 입력되면 다음에 등장할 확률이 높은 단어를 예측함
여기서는 je라고 예측했다고 볼 수 있다. 그리고 이 je가 다음 셀의 입력으로 들어가서 또 예측을 진행, suis를 결과! (반복)
참고로 이것은 테스트 과정 동안의 프로세스를 나타낸다.
seq2seq는 훈련 과정과 테스트 과정의 작동 방식에 차이가 있다.
훈련 과정: context vector와 실제 정답을 동시에 가지므로, 지도 학습 방식을 통해 훈련함! = teacher forcing(교사 학습)
테스트 과정: 훈련 과정과 달리 오직 context vector와 sos 심볼만 입력받은뒤 예측 -> 결과 -> 예측 -> 결과 반복!!
알다 시피 기계는 텍스트보다 숫자 연산에 특화되어 있다. NLP는 기본적으로 tetx -> 벡터로 바꾸는 워드 임베딩을 주로 사용하고 있다 즉, seq2seq에서 사용되는 단어들은 모두 임베딩 벡터로 변환 후 입력으로 사용됨. 아래 그림은 임베딩 층임
(I, am, a student의 embedding layer)
현재 시점을 t라고 할 때, RNN셀은 t-1에서의 은닉 상태와 t에서의 입력 벡터를 입력으로 받고, t에서의 은닉 상태를 만든다.
이때 t에서의 은닉 상태는 바로 위에 또 다른 은닉층이나 출력층이 존재할 경우 위 층으로 보내거나, 필요없다면 무시 할 수있다. 그리고 RNN 셀은 다음 시점에 해당하는 t+1의 셀의 입력으로 현재 t에서의 은닉 상태를 입력으로 보낸다.
(현 시점 t의 은닉 상태는 과거 심저의 동일한 RNN 셀에서의 모든 은닉 값들에 영향을 누적해 받아온 값임)
2. seq2seq 코드 실습
seq2seq2 모델을 설계하고, teacher forcing을 사용하여 훈련시켜보자.
참고로, vocab size 변수에 대한 정보는 아래 그림과 같다.
인코더 코드를 보자.
lstm의 은닉 상태 크기는 256
return_state = True -> 인코더의 내부 상태를 디코더로 넘겨주어야 하기 때문
LSTM에서 state_h, state_c를 리턴받는데, 이는 은닉 상태와 셀 상태에 해당한다.
이 두 가지 상태를 encoder_states에 저장한다.
그리고 encoder_states를 디코더에 전달함으로, 두 가지 상태 모두를 디코더로 전달하게 된다,! (이게 context vector)
디코더 코드)
디코더는 인코더의 마지막 은닉 상태를 초기 은닉 상태로 사용한다.
위에서 initial_state의 인자값으로 encoder_states를 주는 코드가 이에 해당한다고 볼 수 있다.
또한 동일하게 디코더의 은닉 상태 크기도 256으로 설정
테스트(activate) 동작 설계
디코더를 설계해보자.
단어로부터 인덱스를 얻는 것이 아니라 인덱스로부터 단어를 얻을 수 있는 index_to_src와 index_to_tar로 만들기