일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- 플로이드 와샬
- 너비 우선 탐색
- 자바스크립트
- object detection
- 이분 탐색
- c++
- Overfitting
- back propagation
- 2023
- BFS
- 우선 순위 큐
- 알고리즘
- lazy propagation
- 미래는_현재와_과거로
- 크루스칼
- 조합론
- dfs
- dropout
- 세그먼트 트리
- NEXT
- 문자열
- 분할 정복
- 가끔은_말로
- 회고록
- tensorflow
- 다익스트라
- 가끔은 말로
- pytorch
- DP
- 백트래킹
- Today
- Total
Doby's Lab
머신 러닝을 활용한 당뇨병 환자의 관상 동맥 질환 모델 개발 본문
✅ Intro
Medical AI 쪽으로 커리어를 만들어나가고 싶다 말하고, 드디어 처음으로 Medical AI 프로젝트를 해보았습니다.
이번 활동은 daiv에서 이루어졌으며, 저는 2023 Summer Challenge 기수로 참여했습니다.
AI에 관심 있는 분들은 여기서 많은 활동을 해보시길 추천드립니다. 저에게는 많은 걸 얻어갈 수 있는 시간이었습니다.
아래 instagram 링크를 통해 Challenge 모집을 모니터링하시면 됩니다 :)
https://www.instagram.com/team.daiv/
이번 활동에서 저는 Data Engineering과 팀장을 맡았었으며, 전반적인 프로젝트를 기록하되 Data Engineering 중심적으로 기록해 보겠습니다.
✅ Category
- Intro
- Github & Query Design Practice
- Clinical Data
- 프로젝트 주제 선정
- MIMIC-IV Build & Problems
- Query Design
- Conclusion
- Outro
✅ Github & Query Design Practice
이번 프로젝트에서 진행한 Data Engineering과 관련된 SQL 쿼리는 아래 Github에 정리해 두었습니다.
https://github.com/drawcodeboy/PostgreSQL-for-Diabetes-and-CAD
또한, SQL을 사용하는 것이 처음이었기 때문에 Programmers를 통해 10문제를 풀고, 이에 대해 정리하고 생각하는 시간을 가지며 프로젝트를 시작했습니다.
https://draw-code-boy.tistory.com/571
✅ Clinical Data
이번 프로젝트는 Image Data, Signal(=Time-Series) Data가 아닌 Tabular Data를 사용하였습니다. Tabular Data는 엑셀과 같은 정형 데이터의 형태를 띠고 있다고 보시면 됩니다.
이러한 Tabular Data를 Medical 쪽에서는 Clinical Data라 불리며 환자들의 임상적인 정보를 담고 있습니다. 예를 들어, 나이, 성별, 혈액검사 결과, 약물 복용 여부, 입원 및 퇴원 정보 등 여러 종류의 정보를 의미합니다.
여러 종류의 정보를 담고 있는 데이터의 특성상 AI 기반의 이론적 접근뿐만이 아닌 의학적인 도메인과 접근, 즉 데이터 엔지니어링과 분석 능력을 많이 필요로 합니다.
✅ 프로젝트 주제 선정
우리 팀 베이민은 프로젝트를 시작하기 앞서 의료 AI 프로젝트의 전반적인 흐름을 분석하기 위해서 3~4주 간의 논문 스터디를 진행하며 한 주에 한 명씩 돌아가며 논문에 대한 발표를 진행했었습니다.
[리뷰한 논문 리스트]
- Machine Learning Prediction Models for Postoperative Stroke in Elderly Patients: Analysis of the MIMIC Database
- Predicting 30-days mortality for MIMIC-III patients with sepsis-3: a machine learning approach using XGboost
- A Machine Learning Based Discharge Prediction of Cardiovascular Diseases Patients in Intensive Care Units
위와 같은 논문 리뷰를 통해 전반적인 흐름에 대한 인사이트를 가질 수 있었고, 추후 프로젝트 주제 선정에 기반이 될 수 있는 아이디어들을 수집했었습니다.
아이디어들 중 팀원들은 심혈관 질환(Cardiovascular Disease)에 대해 관심을 가져왔으며 각자 이에 대한 조사 후, 심혈관 질환과 많은 부분들이 엮이는 '관상 동맥 질환 (Coronary Artery Disease)'로 주제에 기반을 다졌습니다.
하지만, 이에 대해 이미 많은 연구들이 진행되어 왔으며, 단순히 데이터를 가지고 관상 동맥 질환을 예측하는 프로젝트가 과연 임상적으로 의미를 내놓을 수 있는지에 대한 생각들을 하기 시작했습니다.
그러한 과정 속에서 멘토님께서 동반 질환(Comorbidity)이라는 키워드로 조언을 주신 후에 더 깊은 탐색을 통해 '왜 관상 동맥 질환이 발생하는가?'에 초점을 두었습니다.
우울증 환자들의 관상 동맥 질환 발병은 흡연, 비만 등과 같은 행동적 요인들이 명확하다는 점에서 주제로 택하기에는 아쉬운 부분이라 판단했습니다.
이어서 발견한 당뇨병은 우울증에 비해 조금은 더 의학적인 탐색을 필요로 했기에 주제로 선정하기에 좋은 주제라 생각했습니다.
결론적으로, 우리 팀은 '당뇨병 환자의 관상 동맥 질환 발병 예측'을 메인 주제로 잡았으며, 이후 모델 개발을 통해 'feature importance를 조사하여 어떤 변수들이 환자의 발병에 많은 영향을 미치는가'까지 서브 주제로 정하게 되었습니다.
✅ MIMIC-IV Build & Problems
이번 프로젝트에 사용된 데이터는 MIMIC-IV를 통해 추출하였습니다. MIMIC-IV를 사용하기 위해서는 CITI 교육 수료 과정을 거치면 되는데 어려운 작업은 아니라서 금방 권한을 가질 수 있었습니다. 방법은 링크를 통해 진행했습니다.
Build를 하는 과정에서도 여러 문제점들이 있었으며, 아래와 같이 해결해 나갔습니다.
- Connection Warning: 접속 시에 서버 끊기는 현상
- DBMS에 Data 가져오기
- 7zip 실행 불가 문제
- COPY에 대한 잘못된 이해
- DELETE보다는 TRUNCATE
🤔 1. Connection Warning: 접속 시에 서버 끊기는 현상
쿼리 작업을 위해 psql을 통한 접속이나 pgAdmin을 통해 접속을 할 때, 종종 아래 그림과 같은 알림을 받게 됩니다.
DB와의 connection이 끊겼다는 것을 의미합니다.
이는 Win + R을 통해서 services.msc를 접속하여 postgresql 서비스를 의도적으로 아래 그림과 같이 시작해 주면 해결되는 문제입니다. -> 문제 해결!
🤔 2. DBMS에 Data 가져오기
MIMIC-IV DB 구축을 위해서 MIMIC에서는 가이드라인과 가이드라인 쿼리를 제공합니다. (지금 보니 동영상 튜토리얼도 제공하기 시작한 거 같습니다. 구축을 하시는 분이 해당 글을 보시는 거라면 참고하세요!)
<가이드라인 for MIMIC-III>
https://mimic.mit.edu/docs/gettingstarted/local/install-mimic-locally-windows/
<가이드라인 쿼리>
https://github.com/MIT-LCP/mimic-code/tree/main
위에서 제공하는 쿼리들을 통해 DB를 구축할 수 있었습니다.
저는 PostgreSQL을 통해 작업을 했기 때문에 mimic-iv/buildmimic/postgres
에 있는 쿼리들을 사용했었습니다.
create.sql
을 통해서 빈 스키마와 빈 테이블을 생성하여 테이블 내 필드도 각 타입에 맞게끔 생성할 수 있는 쿼리, 즉 DB 설계를 하는 쿼리를 실행시켰습니다.
load_7z.sql
을 통해서 다운로드한 데이터들을 생성된 테이블에 맞게 올리는 작업을 하는 쿼리입니다. 이 작업 도중에 2가지 문제가 발생하여 많은 문제들을 겪었습니다.
📄 2.1. 7zip 실행 불가 문제
DB에 들어갈 데이터다 보니 애초에 엄청 큰 용량을 차지합니다. 이를 위해 압축이 되어서 제공되기 때문에 구축하는 입장에서는 압축을 해제하고 DB에 올리거나 압축을 해제하면서 동시에 DB에 올릴 수 있도록 해야 합니다.
이에 대해서도 load_7z.sql
에서 해당 역할을 수행하며, 압축 해제를 위해서 7zip을 사용합니다.
여기서 발생한 문제는 7zip이 제대로 작동하지 않는다는 것이었습니다. (아래 그림 - 쿼리 파일 실행시켰을 때)
'7z'은(는) 내부 또는 외부 명령, 실행할 수 있는 프로그램, 또는 배치 파일이 아닙니다.
문제점을 제대로 이해하기 위해 쿼리 파일을 열어보고, 중복되는 커맨드라인들에 패턴을 분석하여 아래와 같이 정리했습니다.
- Command Line
\copy ADMISSIONS FROM PROGRAM '7z e -so ADMISSIONS.csv.gz' DELIMITER ',' CSV HEADER NULL ''
- Command Line 분석
-- target table: 데이터를 로드할 대상 테이블
-- FROM: 데이터를 가져올 로컬 파일의 경로
-- PROGRAM: 외부 프로그램을 실행하여 해당 프로그램의 출력을 DB의 테이블로 복사할 때 쓰는 \copy 명령의 옵션
-- 즉, PROGRAM '7z e -so ADMISSIONS.csv.gz'는
-- 7z라는 외부 프로그램의 실행 결과를 테이블로 카피하겠다는 뜻
-- WITH DELIMITER ',': csv 파일의 필드를 구분하는 데 사용되는 구분자
-- CSV: 데이터 파일의 형식이 CSV임을 알림
\copy [target_table] FROM 'filepath' WITH DELIMITER ',' CSV;
이 과정을 통해서 쿼리에 다른 내용은 문제를 유발하지 않으며, 7zip에 대한 문제가 있음을 확신했었습니다.
그래서 환경 변수에 대한 조사를 더 해보고 나서야 어디서나 파일을 실행 가능하게 하기 위한 환경 변수의 설정에 대해서 잘못된 이해를 하고 있었음을 알게 되었습니다. -> 문제 해결!
📄 2.2. COPY에 대한 잘못된 이해, 이로 인한 중복 데이터 발생
문제 해결을 바탕으로 데이터를 DB에 load 하기 시작하면, 굉장히 오랜 시간이 소요되는 작업임을 알게 됩니다.
이 과정에서 여러 변수들로 인해 작업을 중단했다가 다시 load_7z.sql
을 실행시켜야 하는 일들이 몇 번 있었습니다.
물론, 이 과정에서 COPY 명령어가 중복 데이터를 발생시키지 않는가를 걱정했었습니다만, 이때 당시 너무 급한 나머지 Chat GPT에게 이 사실을 물어보고 '중복 데이터를 발생시키지 않는다'라는 답장을 받았었습니다.
하지만, 나중이 되어서야 '중복 데이터를 발생시키는 거 같다'라는 의구심이 들었고, 몇 가지 테스트와 공식 문서 리뷰를 통해 '같은 데이터에 COPY에 대해서 중복 데이터가 있다'라는 판단을 내렸습니다.
Chat GPT의 신뢰도에 대해서 이렇게 낮다는 것도 잘 알고 있었고, 이를 알고도 너무 급한 나머지 잘못된 판단을 내린 것에 대해 나중이 되어서 더 많은 시간을 쓰게 되었던 문제였습니다. 앞으로는 시간이 걸리더라도 애매한 부분이 없도록 해야 한다는 것을 느꼈습니다.
그리고, COPY 자체가 데이터의 크기에 따라 엄청 가변적이기 때문에 load_7z.sql
을 열어 직접 한 라인씩 실행시켜 주었습니다. -> 문제 해결!
무조건 한 라인 씩(한 테이블 씩) 실행시켜주는 것이 좋습니다! 어떤 변수가 발생할 지 모르기 때문입니다.
🤔 3. DELETE보다는 TRUNCATE
위의 문제들로 인해 여러 번 재구축을 하였습니다. 하지만, 삭제와 같은 과정에서도 생각지도 못한 오류가 존재했습니다.
DB 내에 load 된 데이터를 삭제시키기 위해서 DELETE 명령어를 사용했었습니다.
삭제를 하는 작업임에도 시간이 엄청 오래 걸렸고, DELETE 후에 재구축된 DB에서의 쿼리도 전 DB에 비해 시간이 엄청 오래 소요되었습니다.
이와 같은 이유에는 DELETE라는 명령어의 작동 방식에 있습니다. DELETE는 데이터를 삭제를 하되 그 자리를 빈자리로 공간(Storage)을 남겨두는 방식이며, 로그 데이터를 통해 다시 삭제된 데이터를 불러올 수도 있습니다. 이로 인해 재구축을 여러 번 함에 따라 쿼리의 처리 속도도 엄청나게 느려졌습니다.
저의 의도와 맞게끔 하려면 공간(Storage)을 아예 비워버려야 합니다. 이러한 경우에 쓸 수 있는 명령어가 TRUNCATE입니다. 이 명령어는 테이블과 스키마가 생성된 초기 상태로 돌아가며, 초기 상태의 Storage만 남게 됩니다.
TRUNCATE를 통해 재구축을 했을 때 삭제 속도가 엄청 빨랐고, 안전을 위해 한 라인씩 COPY 한 결과 약 11일 만에 성공적으로 DB를 구축할 수 있었습니다.
추가적으로, DB 내의 자료구조인 INDEX를 index.sql
을 통해 생성해 주어 쿼리의 속도를 높이도록 하였습니다.
✅ Query Design
데이터셋을 만들기 위해서 hosp 스키마에 있는 데이터를 이용했습니다.
그리고, subject_id
와 hadm_id
를 통해 환자를 식별하는 identifier로 사용했습니다.
코드는 Github에 커밋되어 있으니 참고 바랍니다.
1️⃣ Backbone Query
TABLE: dignoses_icd
, d_icd_diagnoses
당뇨병으로 진단받은 환자 중 관상 동맥 질환 발병 유무를 판단하기 위해 작성된 쿼리입니다.
질병에 대한 분류는 국제 질병 분류 기준인 ICD-9, ICD-10을 통해 진행되었습니다.
첫 입원에 대해서만 고려하도록 하였으며, 또한 모든 환자들의 데이터는 당연히 당뇨병 환자입니다.
이 과정에서는 [당뇨병 -> CAD], [CAD -> 당뇨병] 발병 순서를 고려하다가 [첫 입원]을 우선적으로 필터링해야 하는 부분을 놓쳐 논리적인 오류가 있었으며, 데이터의 수를 count 하는 과정에서 이러한 문제점을 알아내었습니다.
즉, [첫 입원] -> [당뇨병 -> CAD]로 고려를 했어야 했습니다. 이 부분에서 깨달은 점은 데이터를 특정 기준으로 필터링을 시켜서 SELECT를 할 때, 어떠한 작업이 우선적으로 실행되어야 하는가를 반드시 따져보아야 합니다.
결론적으로, 해당 쿼리에서는 당뇨병 환자들만 있으며 CAD 발병 유무만 알 수 있도록 작성하였습니다.
2️⃣ Demographic Query
TABLE: admissions
, patients
환자의 나이, 성별, 인종 데이터를 가져오는 쿼리였습니다. 환자의 나이에 대해서 MIMIC은 환자의 익명화가 보장되어야 하는 규제가 있기 때문에 익명화된 데이터에 대해 가이드라인에서 환자의 나이를 구할 수 있는 쿼리를 제공합니다.
pa.anchor_age + DATETIME_DIFF(ad.admittime, DATETIME(pa.anchor_year, 1, 1, 0, 0, 0), 'YEAR') AS age
위와 같이 제공된 쿼리는 문법상 올바르지 않으므로 아래와 같이 수정하여 쿼리를 작성하였습니다.
pa.anchor_age + EXTRACT(YEAR FROM AGE(ad.admittime, make_timestamp(pa.anchor_year, 1, 1, 0, 0, 0)))::smallint AS age
3️⃣ OMR(Online Medical Records) Query
TABLE: omr
해당 테이블에서는 환자들의 기본적인 측정값, 혈압, 체중, 신장, BMI를 제공합니다. 이 중에서 혈압과 BMI를 가져왔습니다.
혈압과 같은 데이터는 이 연구에서 중요하게 적용될 수 있을 거 같은 문제라 수축기 혈압과 이완기 혈압으로 나누어서 진행하였습니다.
또한, 이번 연구의 한계점으로 해당 측정값이 CAD를 진단받기 전인지 후인지 모호한 점이 있었습니다. 그래서 특정 기준을 알 수 없기에 min, max, mean으로 혈압, BMI를 분류하여 추출하였습니다.
4️⃣ Lab event Query
TABLE: labevents
, d_labitems
환자의 표본 검사에 대한 데이터를 담고 있는 테이블입니다. 각 측정값에 대해 어느 정도인지를 데이터로 가져오도록 하였습니다.
테이블이 큰 관계로 모든 환자들에게 표본 검사 결과를 붙인 다음 당뇨병 환자들을 뽑아둔 테이블에서 필요한 작업들을 했습니다.
결측치를 고려하여 검사 결과가 너무 적은 필드에 대해서는 따로 추출하지 않았으며, 이에 대한 기준으로는 각 검사 결과에 대해 count를 했을 때(당뇨병 환자 중), 80,000개 이상인 필드 값만 추출하였습니다. 80,000개 이상이었던 이유는 해당 범위 안에서는 중요할 것이라 예상했던 거의 모든 변수들이 포함되어 있었기 때문입니다.
또한, Lab event도 연구의 OMR과 공통된 연구의 한계점이 있었기 때문에 min, max, mean으로 나누어 추출하였습니다.
5️⃣ Drug Query
TABLE: pharmacy
환자들의 특정 약물에 대한 복용 여부에 대한 데이터를 추출한 쿼리입니다.
emar
, prescriptions
과 같은 다른 약물 관련 테이블도 있었지만 pharmacy
를 채택한 이유는
emar
의 경우 pharmacy
보다는 좀 더 기록에 집중을 한 편이고, pharmacy
에는 있는 데이터가 없는 것들이 있었기 때문입니다.
prescriptions 같은
경우는 전체적인 데이터의 수는 prescriptions
가 더 많았으나 밀도적인 부분에서는 pharmacy
가 더 특정 약물들에 더 집중되어 있었습니다. 이러한 부분에 전반적으로 균일한 테이블 prescriptions
는 pharmacy
보다 많은 부분에서 결측치까지 고려해야 할 것이라 예상했기에 최종적으로 pharmacy
를 채택했습니다.
그리고, Loop Diuretic(이뇨제) 같은 경우는 DB 내에는 존재하지 않았습니다. 하지만, 레퍼런스 논문과 데이터 분석 파트에서 이 약물에 대해 중요하게 여기는 거 같아 이를 대체할 수 있는 다른 이뇨제 약물 5가지를 추출하였습니다.
- Spironolactone
- Bumetanide
- Torsemide
- Furosemide
- Metolazone
6️⃣ Dataset Query (RESULT)
위의 5가지 쿼리로 WITH를 통한 임시 테이블을 만들어 최종적인 데이터셋을 추출하는 쿼리를 작성하였습니다.
그리고, 다른 테이블들은 해당 연구 목적에 있어 적합하지 않다고 판단하였기 때문에 추출하지 않았습니다.
최종적으로, 19,400명의 환자 중 3,929명이 관상 동맥 질환이었으며, 39개의 feature를 포함하고 있는 데이터셋을 추출하였습니다.
✅ Conclusion
제가 추출한 데이터셋을 통해 팀원들이 데이터 분석, 데이터 전처리, 모델링 과정을 거쳤습니다.
언더샘플링 기반의 모델인 BalancedBagging을 통해 Acc: 0.7704, AUC 0.7714라는 결과를 내놓았습니다. 오버샘플링이 아닌 언더샘플링인 이유는 오버샘플링은 없는 데이터를 만들어내 학습을 시키는 것이기 때문에 연구성을 띄는 이 프로젝트에 대해서 연구의 의미를 퇴색시키기 때문입니다.
Feature Importance를 추출한 결과 Clopidogrel(항혈소판제), Age, Furosemide(이뇨제) 등의 순으로 나왔으며, Feature Selection을 통해 Feature Importance 상위 7개의 값을 학습시켰을 때, 최상의 예측 성능을 보였습니다.
✅ Outro
📄 1. 첫 팀 프로젝트
항상 궁금하기도 했던 제 고민은 '팀에서 나는 어떤 모습일까?'였습니다. 그래서 이번이 첫 팀 활동이라 열심히 하려다 보니 팀장까지 맡게 되었습니다. 팀장으로서 부족한 모습들이 스스로 보이기도 했고, 이를 보완하려 노력했던 거 같습니다.
이제 첫 프로젝트도 한 번 겪었으니 다음에도 팀장을 맡는 기회가 있다면, 스케줄적인 측면에서 더 디테일하고 많은 변수들을 고려할 수 있게 되었습니다.
멋진 팀원들 감사합니다!
📄 2. 데이터 엔지니어링의 성취감
처음으로 DB도 구축해 보고 데이터를 추출해 보니 사실 많이 고된 작업이었습니다. 학기 초에 거의 2~3일에 한 번 꼴로 밤을 새웠던 기억이 나네요. 하지만, 이런 와중에 성취감을 느낀 순간이 있었습니다.
만약에 제가 Loop Diuretic이 없어서 그냥 넘어갔다면, Furosemide 약물로 대체할 생각도 없었을 거고, 데이터로 추출되지 않았을 겁니다.
Feature Importance의 상위 3번째에 Furosemide가 추출된 것을 보았을 때, 정말 많이 뿌듯했습니다.
📄 3. 한국통신학회 논문 Accept & 추계종합학술발표회 참가
프로젝트를 끝으로 멘토님께서는 학술대회까지 나가는 게 연구의 의미가 있을 거라 하셨습니다.
프로젝트가 끝나고, 우리 팀은 논문을 작성하여 한국통신학회에 제출하였습니다.
그 결과, Accept... 사실 학회나 논문이라는 게 정말 벽처럼 느껴지는 그런 곳이었는데 논문 쓴 게 Accept까지 된다는 게 정말 말이 안 된다고 생각했습니다. 당연히 학부생 논문이라는 걸 감안해야죠. (학부생 논문이라는 게 있는 지도 몰랐습니다.)
그리고, 다다음주에 추계종합학술발표회에 나갑니다. (쓰면서도 내가 논문 썼다는 게 참)
📄 4. 정말 마지막 Outro
이제 이 글을 마지막으로 3개월 동안 학교 일을 제외하고는 항상 일이 쌓여있는 상태로 살았는데 오랜만에 스케줄이 텅 비네요. (학교 일 제외, 학술대회 제외, 어라 아닌가)
암튼, 하고 싶은 말은 정말 많은 에너지를 쏟아부었습니다. 블로그 기록까지 끝이 보이니 '내가 왜 Medical AI를 하려 했을까'가 다시 상기됩니다. 사람들에게 도움이 되는 기술자가 되어야죠. 오랜만에 저의 본질적인 생각을 돌아보았습니다.
학술대회까지 끝난 게 아니지만, 이제야 이 프로젝트의 막을 내리는 거 같습니다.
끝으로, 항상 많은 도움을 주시는 멘토님께 정말 정말 매우 감사드립니다. 더 많은 노력으로 보답하겠습니다.
저의 첫 Medical AI 프로젝트였습니다.
다음 Medical AI 프로젝트로 돌아오겠습니다. 긴 글 읽어주셔서 감사합니다.
'AI Projects > Project Description' 카테고리의 다른 글
SRL-AE: ECG 데이터 이상치 탐지에 강인한 오토인코더 알고리즘 (1) | 2024.12.22 |
---|---|
U-Net 기반 아키텍처를 활용한 울혈성 심부전 환자 폐부종 진단 방법론 연구 (3) | 2024.04.07 |
Cat & Dog Binary Classification Project Version 2.1 (FINAL) (0) | 2023.01.23 |
Cat & Dog Binary Classification Project Version 2 (1) | 2023.01.07 |
Cat & Dog Binary Classification Project (0) | 2022.12.12 |