같은 숫자를 네 번 재현했는데, 그 숫자가 부풀려 있었다 새 데이터도, 보드도 없는 하루 재학습 날이 다가오고 있었는데, 그날 쓸 새 데이터는 아직 도착하지 않은 상태였다. 노트북 하나로 할 수 있는 일이 뭘까 생각하다가, 그동안 났던 사고 기록을 쭉 모아 봤다. 모아 놓고 보니 전부 같은 모양이었다. 틀렸는데 에러 없이 계속 진행한 경우 였다. 그래서 하루를 이렇게 잡았다. 재학습 날 조용히 틀릴 것들을, 틀리면 그 자리에서 시끄럽게 죽도록 미리 바꿔 놓는 날. 새 기능은 하나도 만들지 않고, 이미 있는 숫자를 다른 방식으로 한 번 더 재 보는 작업이었다. 그 와중에 테스트 성적에 데이터 누수가 있다는 걸 찾았다. 전수 측정 도구를 만들었고, 네 번 재현됐다 먼저 도구 얘기를 해야 한다. 모델이 테스트 데이터 424건을 어떻게 분류하는지(알림이 나가는지, 막히는지, 오분류가 어디로 새는지) 보는 전수 측정은 원래 한 번 돌리고 버린 스크립트였다. 이걸 저장소에 남는 도구로 바꿨다. 넣은 장치는 이런 것들이다. 진짜 모델에 쏘고 있는지 스스로 확인한다. 메모리 사용량의 자릿수, 모델 라이브러리가 실제로 로드됐는지, 같은 입력을 두 번 넣었을 때 출력이 완전히 같은지를 본다. 기준 숫자와 전 칸이 일치해야 정상 종료한다. 일치 판정이 느슨하면 아무거나 통과하니, 일부러 한 칸만 틀리게 바꿔서 실제로 실패하는지도 확인했다. 결과는 424건 전수에서 정상 통과 367, 오알림 27, 막힘 30이었고, 정확도는 0.8868이었다. 이 값이 네 번째 재현까지 전부 일치 했다. 여기까지는 기분이 좋았다. 측정 경로가 안정적이라는 확신이 들었다. 그런데 이 일치가 증명한 건 딱 거기까지였다. 같은 숫자를 안정적으로 재현한다는 것과, 그 숫자가 옳다는 것은 다른 이야기였다. 같은 누수를 네 번 정확하게 재현했을 수도 있었다. 파일 이름이 같이 찍히자 보였다 도구는 결과를 낼 때 파일 이름을 같이 출력하게 만들어 두었다. 그 열이 생기자 눈에 걸리는 게 있었다. 서로 다른 화재경보 파일 17개의 첫 3초 확신도가 전부 0.9809193015098572 였다. 다음 3초도 17개가 전부 같은 값이었다. 확신도가 1.0 근처에서 겹치는 건 이상하지 않다. 숫자 표현이 그 근처에서는 촘촘하지 않기 때문이다. 그런데 0.98 언저리의 값이 소수점 16자리까지 겹치는 건 다른 문제다. 입력이 같았다는 뜻 이다. 파일 해시로 대조해 봤다. 항목 값 전처리 단계 파일 수 2,792개 고유한 내용 2,305개 잉여 487개 (17.4%) 중복 그룹 15개 가장 큰 세 그룹은 한 내용이 각각 108개, 56개, 30개 파일로 퍼져 있었다. 그리고 이 그룹들이 학습/검증/테스트 어디에 들어갔는지 보니 이랬다. 그룹 학습 / 검증 / 테스트 108개 그룹 71 / 20 / 17 56개 그룹 38 / 9 / 9 30개 그룹 19 / 4 / 7 중복 15그룹 중 10그룹이 경계를 가로질렀다. 같은 내용이 학습에도, 검증에도, 테스트에도 들어가 있었다. 나머지 5그룹은 학습 안에만 갇혀 있었고 전부 파일 2개짜리, 잉여 5개였다. 데이터량을 줄이는 문제는 사실상 없고, 문제는 경계를 넘은 쪽에 있었다. 결정타는 이거였다. 테스트 424건 중 79건이 학습·검증 데이터와 바이트 단위로 같았고, 전부 화재경보였다. 초인종과 노크는 0건이었다. (여러 파일이 앞부분을 공유하는 것으로 보이긴 하는데, 왜 그렇게 됐는지는 확정하지 못했다. 이 중복이 데이터를 모으는 단계에서 생겼는지 전처리 단계에서 생겼는지도 모른다.) 얼마나 부풀려졌나 — 그리고 아침에 내가 한 과장 이 숫자를 처음 봤을 때 나는 "정확도의 상당 부분이 암기일 수 있다"고 말했다. 실측해 보니 과한 표현이었다. 누수 79건의 정확도는 1.0000 , 전부 맞혔다. 나머지 청정 345건의 정확도는 0.8609 였다. 전체 정확도는 0.8868에서 청정 기준 0.8609로, 2.6%p 내려간다. 클래스 균형을 보는 지표(macro F1)는 0.8478에서 0.8367로, 0.011 내려간다. 화재경보만 흔들린다. 그 클래스의 F1은 0.927에서 0.893으로 내려가고, 평가 건수는 254에서 175로 줄어든다. 그리고 데모 주력인 초인종(F1 0.736)과 노크(0.881)는 소수점 아래까지 한 글자도 변하지 않았다. 누수가 그 두 클래스에 0건이었으니 당연한 결과다. 동시에 청정 값을 가려내는 계산이 맞게 돌았다는 검산이기도 했다. 건드리지 않아야 할 곳이 안 움직였다. 여기서 구분해 둘 게 있다. 79건이 전부 맞았다는 건 실측이다. 왜 전부 맞았는지, 그게 암기 때문인지는 아직 추정이다. 이 둘을 한 문장에 섞지 않으려고 했다. 그리고 누수가 있다 는 사실과 성적이 크게 틀렸다 는 주장도 별개였다. 아침에는 앞의 것을 보고 뒤의 것까지 말해 버렸고, 숫자를 보고서야 크기를 제대로 쟀다. 설계가 고장난 게 아니라, 범위 밖이었다 학습과 테스트를 나눌 때는 같은 원본에서 나온 조각이 양쪽으로 갈라지지 않게 파일 이름 기준으로 묶어서 나눈다. 이 방어는 설계대로 작동했다. 다만 내용이 같고 이름이 다른 파일 은 애초에 이 방어의 사정권 밖이었다. 그래서 기록에도 "설계 결함"이 아니라 "방어 범위 밖"이라고 적었다. 두 표현은 그 뒤의 대응을 다르게 이끈다. 결함이라고 하면 설계를 갈아엎는 쪽으로 가고, 범위 밖이라고 하면 방어를 한 겹 더 얹는 쪽으로 간다. 이번 경우에는 후자가 사실에 맞았다. 아직 다 된 건 아니다 누수는 발견과 정량까지다. 대응은 아직 정하지 않았다. 중복을 제거하고 다시 나눠 재학습할지, 누수를 고지하고 그대로 쓸지, 청정 값을 정본으로 바꿀지 모두 미결이다. 데이터는 한 줄도 건드리지 않았고 재학습도 하지 않았다. 청정 정확도 0.8609도 확정 숫자가 아니다. 발표에 어떤 숫자를 쓸지는 개발이 끝난 뒤 정한다. 증강본과 최종 데이터셋에 같은 중복이 있는지는 아직 보지 않았다. 이 측정은 노트북에서 서버 없이 모델만 직접 호출한 것이다. 보드는 쓰지 않았고 실제 알림 경로를 통과한 게 아니다. 기기와 시연 쪽 상태는 이날 달라진 게 없다. 79건이 전부 맞았다는 것과, 그 이유가 암기라는 것은 다른 층위다. 앞은 실측이고 뒤는 추정이다. 마무리 이날 한 일은 새 기능이 아니라 이미 있던 숫자를 다른 방식으로 한 번 더 재 본 것뿐이었다. 그랬더니 성적이 부풀려 있었다. 그중 가장 크게 남은 건 네 번 재현된다는 사실이 그 숫자가 옳다는 증거가 아니라는 것 이었다. 재현이 알려 주는 건 측정이 안정적이라는 사실이고, 무엇을 재고 있는지는 알려 주지 않는다. 이번에는 그걸 파일 이름 한 열이 알려 줬다. 띵동(Ddingdong)은 청각장애인 1인 가구를 위한 현관 부착형 소리 분류 알림 시스템 졸업작품입니다. 초인종 · 노크 · 화재경보를 구분해서 사진과 자막까지 스마트폰으로 보내주는 걸 목표로 만들고 있습니다.
고치지 못한 실험이 진척이었던 이야기 이날 "고쳤다"는 결과는 거의 없었다 마이크에는 오래 남아 있던 문제가 하나 있었다. 소리가 없는 순간에도 가끔 최대치로 튀는 값이 하나씩 끼어드는 현상이다. 원인 후보는 여럿이었고, 이날은 그중 하나를 결선을 건드리지 않고 가려 보기로 했다. 후보는 이렇다. 마이크의 데이터 선이 출력을 내보내지 않는 구간에서 어디에도 연결되지 않은 채 떠 있고, 그래서 값이 흔들린다는 가설이다. 같은 날 다른 것들도 재 봤다. 그런데 하루가 끝나고 정리해 보니 결과가 전부 비슷한 모양이었다. 무언가가 해결된 게 아니라 후보 하나가 약해졌거나, 어디까지는 아니라는 경계가 그어진 것들이었다. 이 글은 그중 마이크 실험 하나에 대한 기록이다. 후보를 지우는 실험은 이렇게 짰다 데이터 선이 떠 있어서 생기는 문제라면, 선을 약하게 눌러 주면 줄어들어야 한다. 칩 안에는 그런 용도로 쓸 수 있는 약한 내부 저항이 있었다. 납땜 없이 설정 하나로 켜고 끌 수 있다. 그래서 모드를 이렇게 나눴다. 기본 : 아무것도 바꾸지 않은 상태 저항 켬 : 데이터 선을 땅 쪽으로 약하게 눌러 주는 상태 센서 끔 / 센서 끔+저항 켬 : 사람 감지 센서의 측정을 멈춘 대조 조건. 튀는 값이 센서 쪽에서 오는지 보려는 것이다 측정 순서는 이랬다. 기본 → 저항 켬 → 기본 → 저항 켬 → 센서 끔 → 센서 끔 + 저항 켬 (각 블록마다 스냅샷 3회) 기본을 두 번 넣은 이유 가 이 설계의 핵심이다. 한 조건을 몰아서 연달아 재면 "조건 차이"와 "시간 차이"가 섞인다. 세션이 길어지면 환경도 달라지기 때문이다. 교대로 배치하면 그 둘을 가를 수 있다. 그리고 판정의 첫 관문은 비교가 아니라 재현 이었다. 기본 조건에서 튀는 값이 다시 나오지 않으면, 그 뒤의 비교는 아무것도 말해 주지 않는다. 교대로 두 번씩 재도 기본 조건 6번 모두 튀는 값이 나왔고, 그래서 비교가 성립했다. 결과: 줄어들지 않았다. 그런데 한 가지는 바뀌었다 스냅샷 18개가 전부 유효했고, 튀는 값의 개수는 이랬다. 조건 튀는 값 개수 (스냅샷별) 기본 (1차 / 2차) {1, 4, 5} / {2, 1, 5} 저항 켬 (1차 / 2차) {3, 5, 5} / {0, 1, 5} 센서 끔 0, 0, 0 센서 끔 + 저항 켬 0, 0, 0 저항을 켜도 개수는 기본과 같은 범위였다(0 5개 대 1 5개). 센서 측정을 끄면 저항과 상관없이 전부 0개였다. 그래서 "데이터 선이 떠 있어서 생긴다"는 후보는 이 실험으로는 지지되지 않았다. 그리고 튀는 값이 센서 측정과 같이 움직인다는 단서는 덤으로 얻었다. 그런데 한 가지는 달라졌다. 저항을 켜면 각 값의 끝자리 비트가 0으로 눌렸다. 끝자리가 0인 비트 수가 기본에서 5~6개이던 것이 저항을 켜면 7개 안팎(센서 끔+저항 켬에서는 8개)이 됐다. 약한 저항이 실제로 일을 하고 있었다는 뜻이다. 이 끝자리 비트들은 어차피 소리 값으로 변환하는 과정에서 버려지므로 소리 자체에는 영향이 없을 가능성이 높다. 이건 실증이 아니라 논증이다. 정리하면 "저항은 동작했고, 튀는 값은 별개의 경로일 수 있다"까지가 이 실험이 말해 주는 범위다. 여기서 조심할 게 있다. 칩 안쪽 저항은 값이 약하다. 문서가 권하는 외부 저항이었다면 결과가 달랐을지는 여전히 모른다. 그래서 "저항은 소용없다"로 읽히지 않도록 했다. 실험이 문제를 고치지는 못했지만 후보 목록에서 하나가 줄었고, 그게 이날의 진척이었다. 측정값에 섞여 들어온 것들 실험 설계가 깔끔했던 것과 별개로, 측정 현장은 깔끔하지 않았다. 시야에 물건이 있었다. 측정 전에 사람 감지 센서 값을 보니 앞쪽 물건 때문에 "감지됨/아님"이 2초마다 뒤집히고 있었다. 물건을 치우자 구역 수치가 7 8에서 0 4로 내려가 안정됐다. 이걸 확인하지 않고 쟀으면 센서 쪽 요동이 마이크 결과에 섞였을 것이다. 스냅샷 하나가 오염됐다. 튀는 값을 뺀 소리 크기가 다른 스냅샷의 약 6배였다. 그 순간 실제 소리가 들어갔다는 추정이다. 다만 무슨 소리였는지는 기록이 없다. 그래서 그 스냅샷의 개수는 참고용으로만 표시했다. 첫 시도에서는 스냅샷이 0줄이었다. 기록 파일을 열어 보니 소리 줄이 하나도 없어서 놀랐는데, 키 입력이 모니터 창을 클릭해 활성화하기 전에는 기기에 전달되지 않았다. 기록은 정상이었고 입력이 들어가지 않았을 뿐이었다. 기록 방식도 바꿨다. 화면을 복사해 붙이는 대신 터미널 기록 명령으로 파일에 실시간으로 이어 쓰게 했다. 소리 줄과 센서 줄이 섞여 나오는 출력이라 복사하다 빠뜨릴 위험이 컸다. 아직 다 된 건 아니다 이 실험은 후보 하나를 약하게 만든 것이지 원인 규명도 해결도 아니다. 튀는 값의 해결책은 아직 정해지지 않았다. 칩 안쪽의 약한 저항으로 한 실험이다. 외부 저항의 효과는 판정하지 못했다. 끝자리 비트가 소리 값에 영향이 없다는 건 논증이고, 실증하지 않았다. 스냅샷 하나의 오염은 추정이다. 그때 어떤 소리가 있었는지 모른다. 마무리 이날 마이크 쪽에서 얻은 건 "여기까지는 아니다"였다. 후보 목록에서 하나가 약해졌고, 튀는 값이 센서 측정과 같이 움직인다는 단서가 하나 생겼으며, 측정 현장에서 걸러야 할 오염이 몇 가지 드러났다. 고친 건 없다. 그래도 교대 배치로 시간 차이를 가르고, 먼저 문제가 재현되는지를 확인하고, 말할 수 있는 범위를 끝까지 좁혀 적고 나니 다음 후보로 넘어갈 수 있었다. 실험이 문제를 고치지 못해도, 설계가 맞았다면 후보 하나는 줄어든다. 띵동(Ddingdong)은 청각장애인 1인 가구를 위한 현관 부착형 소리 분류 알림 시스템 졸업작품입니다. 초인종 · 노크 · 화재경보를 구분해서 사진과 자막까지 스마트폰으로 보내주는 걸 목표로 만들고 있습니다.
들어가기 앞서 오늘은 개인프로젝트의 주제선정이 완전 종료된 이후 계속 진행하여 중간점검까지 진행하는 과정이였습니다. 주제 전역자 청년정책 가이드 챗봇 중간 점검 한 줄 상태 주제와 현재 상태(순조로움/지연/막힘) 선택 ᄂ 지연 - 수요일 마지막에 수정사항을 받은 뒤 해당하는 정책의 자료들을 찾다 지연0 여태까지 한 내용 / 앞으로 할 내용 체크리스트 완료/진행중/안함(이유) 표시 ᄂ 현재 7단계로 나누고 이후 세부항목으로 나눔 주제 확정 - 정책 주제 선정 → 완료 데이터 구축 - 7가지의 세부항목으로 나눔 온통청년 API 전체 수집 → 완료 서울, 중앙부처 정책 분리 → 완료 1단계 사용 정책 선정 → 완료 군 관련 특례 여부, 개수 확인 → 완료 군 관련 정책 공고문 수집 → 완료 정책 종료, 변경 안내문 수집 → 미진행 충청권, 전국 정책 확장 → 미진행 파싱, 정제, 청킹 - 정책, 문서 파싱, 정제, 청킹 → 완료 특례 규칙 - 전역자 특례 규칙 표 작성 → 완료 임베딩 - 임베딩, 백터 DB 적재 → 완료 평가 - 평가셋 완성 → 진행중 개선 - RAG 성능 비교 → 진행중 데이터 현황 - 수집한 문서와 출처 주요 데이터 수집 - 온통청년 api 이외의 정책과 출처 서울권 공고문 - 서울 특별시, 청년몽땅정보통 등 중앙부처 공고문 - 국토교통부, 한국문화예술위원회 웹페이지 본문 - 청년몽땅정보통 - 서울시, 제대군인지원센터 - 정부24 - 고용복지플러스센터, iMND(중앙부처) 참고 자료 - 온통청년 API 코드정의서 평가셋 문항 수 30 → 정답 20 애매 5 오답 5 정답과 근거 위치 함정문항 등 → 표 참조 숫자 표 GPT 빈손테스트(문서 학습 없이 질문 한 내용) ᄂ 각각 1, 9, 10번 내용 질문(GPT, Gemini) * 1번 둘다 정답 * 9번 둘다 정답 * 10번 GPT → 국토교통부 근거 → 국토교통부를 근거로 들고왔고, 해당 정책 pdf를 제시했지만 안에 전역군인 관련 정책은 없음 gemini 오답 → 서울주거포털 근거 → 서울주거포털이 제시하는 청년월세 정책과 국토교통부가 제시하는 청년월세정책은 엄연히 다름 ᄂ 그래서 질문도 국토교통부를 근거로 제시함 * 고치기 전 RAG ᄂ 첫 평가 결과는 정확 6 / 부분 정확 16 / 오답 8 → 원인을 정리해보니 제 코드에서 전제 확인 지침을 조건문에 빠뜨린 실수, 문화패스 14번의 연도별 기준 누락, 나이 미상 시 판단 통째 생략, 판정 사유 문장 오독, 세부 조건 누락 등이 발견 * 고치고 난 후 RAG ᄂ 정확 23 / 부분 정확 6 / 오답 1 → 오답 유도 문항은 5개 모두 정확 남은 오답 1개와 부분 정확 문항을 확인 오늘 후기 이제 개인프로젝트의 주제를 완전히 선정하고 이제 계속 프로젝트를 개발하며 중간 점검까지의 과정이였습니다. 우선 프로젝트를 선정하고나니 고려해야할 상황이나 수정 사항등 고칠부분과 고려할 부분이 많아 다소 지연되어 현재도 고칠 사항 몇몇이 계속 파악되고 있습니다. 이 부분은 다음주에도 계속 고쳐 중간점검까지 이어가겠습니다.
웹페이지의 모든 요소는 눈에 보이지 않는 사각형 박스로 구성 됩니다. 이 사각형 박스에 적용되는 규칙이 박스모델인데, 이 박스 모델(Box Model)은 웹 브라우저가 HTML의 모든 요소를 화면에 렌더링할 때 ‘네모난 상자’로 처리하는 기본 규칙 입니다. HTML 태그가 <div> 이든, <button> 이든, <h1> 이든 무엇이든 상관없이 모든 웹 요소는 중심부에서 외곽으로 확장되는 4개의 Layer(층) 구조로 이루어져 있습니다. [박스 모델 구성 요소 (4개의 Layer)] 1) Content (콘텐츠): 텍스트, 이미지, 아이콘 등 실제 내용물이 들어가는 영역 width와 height 속성으로 크기를 직접 조절 2) Padding (안쪽 여백): 콘텐츠 영역과 테두리(border) 사이의 내부 여백 요소를 클릭할 수 있는 영역 확장, 배경색/배경 이미지가 (이 영역까지) 적용됨 3) Border (테두리): Padding과 Margin 경계에 위치하는 외곽선 border: 1px solid black; 처럼 두께, 스타일, 색상 지정 4) Margin (바깥쪽 여백): 현재 요소와 다른 이웃 요소 사이의 외부 간격 투명한 영역이며, 요소 상하 마진이 겹칠 때 더 큰 마진으로 합쳐지는 '마진 병합(Margin Collapse)' 현상 발생 [박스 모델을 반드시 알아야 하는 3가지 이유] 박스 모델을 이해하지 못하면 웹 화면 레이아웃을 잡을 때 수많은 버그와 마주치게 됩니다. 1 요소를 원하는 크기로 정확히 제어하기 위해 (크기 계산 착오 방지) 기본적으로 CSS에서 width: 200px을 주고 padding: 20px, border: 2px를 추가하면 브라우저가 화면에 실제로 그려내는 전체 너비는 200 + 40 + 4 = 244px가 됩니다. 이 원리를 모르면 "너비를 분명 200px로 정했는데 왜 다른 200px 상자보다 더 크게 보이고 줄바꿈이 깨질까?"라는 문제를 겪게 됩니다. 2 안쪽 여백(Padding)과 바깥 여백(Margin)의 용도를 정확히 나누기 위해 Padding: 요소 내부 디자인을 가다듬을 때 씁니다. (예: 버튼 안의 글자와 버튼 테두리 사이가 너무 빽빽해서 답답할 때) Margin: 다른 요소와의 거리를 둘 때 씁니다. (예: 위쪽 카드가 아래쪽 카드와 너무 바짝 붙어있을 때) 이 둘을 헷갈려서 Margin을 줘야 할 곳에 Padding을 주면 배경색이 이상하게 확장되는 스타일 오작동이 일어납니다. 3 마진 병합(Margin Collapse) 현상을 해결하기 위해 두 개 이상의 세로로 인접한 요소가 각각 margin-top과 margin-bottom을 가질 때, 두 마진 값이 더해지는 것이 아니라 더 큰 쪽의 마진 하나만 덮어씌워져 적용되는 현상 이 발생합니다. 예: 위 상자의 margin-bottom: 30px과 아래 상자의 margin-top: 20px이 만나면 50px이 아닌 30px 간격만 생깁니다. 박스 모델의 원리를 알아야 이 현상을 버그로 오인하지 않고 대처할 수 있습니다. [박스 전체 크기 계산 (content-box 기준)] 실제 박스 너비 = Content + 좌우 Padding + 좌우 Border + 좌우 Margin 기본 CSS 동작(box-sizing: content-box)에서는 요소의 전체 너비가 width + padding + border의 합으로 계산되어 사용자가 지정한 width보다 실제 상자가 커지는 문제가 자주 생깁니다 . 이 때문에 실무에서는 padding과 border를 포함하여 지정한 width 크기로 상자를 고정해 주는 속성을 프로젝트 최상단에 적용하고 시작하는 것이 표준(box-sizing: border-box;) 입니다 [CSS 기술 예시] /* 실무 필수 기본 설정 - padding과 border를 포함 전체 넓이를 width로 기본 계산 / ` ` { box-sizing: border-box; } box-sizing: border-box 속성은 필히 CSS 최상단에 표기함을 기억해야 합니다. 참고로 '*' 는 전체 선택자로 이 선택자에 선언된 내용이 전체에 적용됨을 의미합니다.
주문 화면에 고객 이름을 표시하려고 합니다. 고객 이름은 고객 표에, 주문 번호는 주문 표에 들어 있습니다. 두 표를 연결하면 이름과 주문 번호를 한 줄에 함께 보여 줄 수 있습니다. DB(Database)는 자료를 저장하고 찾는 시스템입니다. 표의 한 줄은 행, 이름이나 번호처럼 같은 종류의 값을 담는 항목은 열이라고 부릅니다. SQL은 DB에 작업을 요청하는 언어입니다. 이번 글에서는 SQL의 JOIN으로 관련 행을 연결하고, 연결 조건에 따라 결과가 어떻게 달라지는지 확인하겠습니다. 앞선 Index 글 에서는 원하는 행을 찾는 보조 자료구조인 Index를 살펴봤습니다. 이번에는 찾은 행들이 어떤 짝으로 연결되는지에 집중하겠습니다. 고객 번호로 주문을 연결하기 JOIN은 여러 표의 관련 행을 묶어 읽는 작업입니다. 고객 표에는 세 명이 있고, 주문 표에는 세 건이 있다고 하겠습니다. 이름과 주문은 설명을 위해 만든 가상 자료입니다. 고객 번호 이름 1 가람 2 나래 3 다온 주문 번호 고객 번호 결제 상태 101 1 PAID 102 1 CANCELLED 103 2 PAID PAID 는 결제 완료, CANCELLED 는 취소를 뜻합니다. 고객 번호 1은 주문 101과 102에 모두 들어 있습니다. 따라서 가람의 행은 두 주문과 각각 연결됩니다. ON 은 행을 연결할 조건을 적는 부분입니다. 여기서는 고객 표의 번호와 주문 표의 고객 번호가 같을 때 연결합니다. INNER JOIN 은 조건에 맞는 짝을 결과로 내보냅니다. 고객 3은 연결할 주문이 없으므로 이 예제의 결과에는 고객 1과 2의 주문 세 건이 들어갑니다. SQLite JOIN 규칙 SQL로 세 쌍 확인하기 SQLite는 DB를 관리하는 프로그램입니다. 아래 코드는 SQLite의 새 Memory DB에서 실행했습니다. Memory는 실행 중에 자료를 담는 공간입니다. 예제 전용 공간에서 고객과 주문 표를 만들었습니다. CREATE TABLE customer ( id INTEGER PRIMARY KEY, name TEXT NOT NULL ); CREATE TABLE orders ( id INTEGER PRIMARY KEY, customer_id INTEGER NOT NULL, status TEXT NOT NULL ); INSERT INTO customer VALUES (1, '가람'), (2, '나래'), (3, '다온'); INSERT INTO orders VALUES (101, 1, 'PAID'), (102, 1, 'CANCELLED'), (103, 2, 'PAID'); SELECT c.id AS customer_id, c.name, o.id AS order_id FROM customer AS c INNER JOIN orders AS o ON c.id = o.customer_id ORDER BY c.id, o.id; CREATE TABLE 은 표를 만들고, INSERT 는 행을 넣습니다. INTEGER 는 정수, TEXT 는 글자 값을 담습니다. PRIMARY KEY 는 행을 구분하는 기본 열을 지정합니다. NOT NULL 은 값이 없음을 나타내는 NULL 의 입력을 제한합니다. SELECT 는 읽을 열을, FROM 은 읽을 표를 정합니다. AS 는 짧은 이름을 붙입니다. c 는 고객 표, o 는 주문 표를 가리킵니다. 따라서 c.id 는 고객의 번호이고, o.customer_id 는 주문에 적힌 고객 번호입니다. ORDER BY 는 출력 순서를 정합니다. 여기서는 고객 번호, 같은 고객 안에서는 주문 번호의 작은 순서로 출력합니다. 실제 반환 값은 다음과 같았습니다. customer_id name order_id 1 가람 101 1 가람 102 2 나래 103 주문이 없는 고객도 남기기 전체 고객 목록을 보려면 LEFT JOIN 을 사용할 수 있습니다. 왼쪽 표의 행을 남기고, 연결할 오른쪽 행이 있으면 그 값을 붙입니다. 연결할 주문이 없는 고객 3의 주문 열에는 NULL 이 들어갑니다. NULL 은 해당 값이 없거나 알 수 없음을 나타내는 표시입니다. SELECT c.id AS customer_id, c.name, o.id AS order_id FROM customer AS c LEFT JOIN orders AS o ON c.id = o.customer_id ORDER BY c.id, o.id; 이 조회는 앞의 세 행에 (3, 다온, NULL) 을 더해 네 행을 반환했습니다. 고객 1은 연결된 주문이 두 건이므로 여전히 두 행을 차지합니다. 그림 아래에서 화살표 하나는 고객과 주문의 연결 하나를 뜻합니다. 고객 1이 두 줄에 등장하는 이유는 연결된 주문이 두 건이기 때문입니다. 결과 행 수를 읽을 때는 고객 수와 연결된 짝의 수를 구분해야 합니다. 결제 조건을 어디에 적을까? “전체 고객을 보여 주고, 결제 완료 주문만 붙이기”라는 요청을 생각해보겠습니다. 결제 상태를 ON 에 적으면 연결할 주문을 고르는 조건이 됩니다. SELECT c.id AS customer_id, c.name, o.id AS order_id FROM customer AS c LEFT JOIN orders AS o ON c.id = o.customer_id AND o.status = 'PAID' ORDER BY c.id, o.id; AND 로 연결한 두 조건을 모두 만족하는 주문이 붙습니다. 결과는 (1, 가람, 101) , (2, 나래, 103) , (3, 다온, NULL) 입니다. 고객 3은 결제 완료 주문이 없어도 왼쪽 표의 행으로 남습니다. WHERE 는 결과에 남길 행의 조건을 적습니다. 같은 결제 조건을 WHERE 에 적으면 연결 결과에서 결제 완료인 행만 남깁니다. SELECT c.id AS customer_id, c.name, o.id AS order_id FROM customer AS c LEFT JOIN orders AS o ON c.id = o.customer_id WHERE o.status = 'PAID' ORDER BY c.id, o.id; 반환 값은 (1, 가람, 101) , (2, 나래, 103) 두 행입니다. 고객 3의 결제 상태는 NULL 입니다. 이 행은 PAID 라는 조건을 만족하지 않아 결과에서 빠집니다. LEFT JOIN에서 오른쪽 표의 조건을 적을 때는 모든 고객을 남길지, 조건을 만족하는 연결만 남길지 먼저 정해야 합니다. PostgreSQL도 DB 관리 프로그램입니다. 연결 규칙은 공식 문서를 참고했고, 위 네 조회는 SQLite에서 실행했습니다. PostgreSQL 18의 ON과 WHERE 예제 그림과 설명은 결과를 이해하기 위한 연결 규칙입니다. 실제로 자료를 읽는 순서는 DB가 선택한 실행 계획에 따라 달라집니다. 이 글에서는 반환 값을 확인했으며, 조회 시간은 따로 측정하지 않았습니다. 확인 문제 고객 1에게 주문 104를 추가하면 INNER JOIN의 전체 결과는 몇 행일까요? 주문 표의 모든 행을 지우면 LEFT JOIN에는 고객이 몇 명 남을까요? 전체 고객을 유지하면서 결제 완료 주문만 연결하려면 결제 조건을 어디에 적을까요? 답과 해설 네 행입니다. 고객 1은 세 주문과 연결되고, 고객 2는 한 주문과 연결됩니다. 세 명입니다. 각 고객의 주문 열에는 NULL이 들어갑니다. 연결한 주문이 있는지 확인할 때는 주문 번호 열을 읽을 수 있습니다. ON에 적습니다. 연결할 주문을 결제 완료로 제한하고, LEFT JOIN으로 전체 고객을 남깁니다. WHERE에 적은 결제 조건은 연결한 결과에서 조건을 만족하는 행만 남깁니다. 오늘은 SQL을 실행하기 전에 네 조회의 결과를 종이에 적어보겠습니다. 하루 뒤에는 고객 3에게 주문을 하나 추가하고 바뀌는 행을 예상해보겠습니다. 일주일 뒤에는 목록 화면의 행 하나가 고객 한 명인지, 고객과 주문의 한 쌍인지 설명해보면 좋겠습니다. 이어지는 학습 주제는 Composite Index, 즉 여러 열을 정해진 순서로 묶은 Index의 열 순서입니다. 자료 확인: 2026-10-07, SQLite 공식 문서와 PostgreSQL 18 공식 문서. 예제 검증 환경: 프로그램을 작성하는 언어인 Python 3.12.14, SQLite 3.53.1, 독립 :memory: DB. 위 네 조회의 반환 값과 주문 추가·빈 주문 표의 결과를 확인했습니다.
cnn 모델의 추론 loss신호가 불안정하거나 gan rl 학습이 조금 어렵다 diffusion rl value 를 띠리긴다 손실함수란? 모델이 예측한값과 실제값의 차이를 수치로 표현한 함수 클래식케이션 크로스 엔트로피 mse 우리가 정말 풀고싶은 문제 클래스피케이션 정답률 최대화 머신 트렌셀레이션 적확한 문장 생성 진짜 풀고싶은것 목적 함수 서로게이트로스 로스 펑션 미분 가능 그레디언트를 사용하는 역전파가 가능 방향이 정확하지 않을수있지만 어디로 가라라는 지시 자체는 확고하다 클래스피케이션 크로스 엔트로핀 0.5를 기준 그래드인트 디선트 ![] ![] 기울기가 0인 부분을 최솟값 미분해서 0이되는 지점을 찾아서 편미분 1억번 보통은 직접 미분보다는 그래디던트 디선트 w가 t1 아주작게 감소 시켯을 때 los를 구핧수있다 미분을 안하고도 기울기를 계산 할수있다 t2를 찍고 실제로스를 전체 데이터를 그레디언트를 계산하는게아니라 배치 데이터로 계산하는SGD 를 사용 이유는 계산 효율 메모리 절약 로컬 미니멈 비슷한 방향으로 업데이트
2026년 10월 7일 오전 11시 43분경 전골이 전담의 문워크를 따라하며 "호-호"라는 아담을 따라하는 말을 했습니다. 그러자 아담은 매우 화가 나서 초속 408M의 속도로 공중제비로 날아가서 전골에게 문워크 이단 옆차기로 전골을 단번에 쓰러트렸습니다. 그러자 전골은 "흐흐헿 역시 나는 잘생겼다"라는 조현병 같은 말을 했습니다. 그때 그 말을 듣고 화가 난 전세가 가오를 부리지 않고 바로 창문을 깨고 나와 전골에게 헤드락을 걸어 제압을 했습니다. 전골은 위기를 느끼고 입을 벌려 냄새로 전세와 전담을 도망가게 했습니다.