OpenAI와 Django를 활용한 맞춤형 장학금 추천 로직 구현

yeomm·2024년 11월 21일

졸업 프로젝트

목록 보기
4/4

졸업 프로젝트 주제

“자신에게 적합한 장학금을 찾는 대학생을 위해 생성형 AI를 이용하여 각 사용자에게 맞는 장학금을 추천하고 이전 수혜자들의 조언을 바탕으로 장학금 수혜 팁을 제공해주는 서비스”

서비스의 주요 기능

  • 통합된 장학 정보 제공
  • 사용자 프로필 맞춤 장학금 추천 기능
  • 이전 수혜자들의 조언을 통한 수혜 팁 제공 기능

⇒ 이 중 두 번째 기능의 구현 방법을 작성해보려고 한다.

사용한 프레임워크 및 기술

  • OpenAI의 GPT-4o-mini 모델
  • Django

OpenAI를 호출하여 GPT-4o-mini 모델을 사용하여 프롬프트 엔지니어링을 통해 추천 로직을 구현했다. 이를 Django 프레임 워크를 활용해 웹 애플리케이션과 연동했다.

아래에서 자세한 과정에 대해 설명하겠다.

장학금 데이터 형식

    {
      "대학구분": "해당없음",
      "모집시작일": "2024-05-07",
      "모집종료일": "2024-05-27",
      "번호": 1515,
      "상품구분": "장학금",
      "상품명": "주거비지원 장학금",
      "선발방법 상세내용": "○ 생활정도 100점 (평가요소 : 소득분위)※ 기초수급자 100점/차상위계층 98점 부여○ 동점자 처리기준 : 화성시 거주기간에 따라 점수 차등 부여",
      "선발인원 상세내용": "○ 100명",
      "성적기준 상세내용": "해당없음",
      "소득기준 상세내용": "○ 소득분위 6분위 이내의 대학생",
      "운영기관구분": "지자체",
      "운영기관명": "재)화성시인재육성재단",
      "자격제한 상세내용": "○ 한국장학재단 2024년 1학기 학자금 지원구간 통지서 미제출자○ 대학교 재학 상태가 아닌 경우",
      "제출서류 상세내용": "○ 주민등록초본○ 재학증명서○ 2024년 1학기 학자금 지원구간 통지서 또는 저소득층 증빙서류○ 본인 또는 부모·친권자 명의의 주거현황 증빙서류○ 신청자 기준의 가족관계증명서(해당자)※ 자세한 사항은 홈페이지 공고문 참조",
      "지역거주여부 상세내용": "○ 본인 또는 부모·친권자 중 1명이 주민등록상 공고일 기준 1년 이상 화성시에 계속 거주하고 있는 자",
      "지원내역 상세내용": "○ 200만원(학기별 분할 지급)※ 생활비성 장학금",
      "추천필요여부 상세내용": "해당없음",
      "특정자격 상세내용": "○ 비수도권 소재 대학교에 재학하고 있는 대학생(전학년)※ 비수도권 : 수도권(서울·경기·인천) 이외의 국내지역○ 공고일 포함 2024년 재학기간(1·2학기) 내 학교 내 인근 거주 증빙이 가능한 자※ 자세한 사항은 홈페이지 공고문 참조",
      "학과구분": "해당없음",
      "학년구분": "해당없음",
      "학자금유형구분": "지역연고",
      "홈페이지 주소": "https://www.hstree.org"
    }

장학금 데이터 형식은 위와 같고, 위의 데이터 형식을 바탕으로 추천 로직을 구현했다.

추천 로직

사용자 프로필을 바탕으로 사용자와 맞는 장학금 추천 로직 순서는 다음과 같다.

1. 사용자가 입력한 날짜로 필터링

def filter_scholarships_by_date(current_date, scholarships):
    filtered_scholarships = scholarships.filter(
        recruitment_start__lte=current_date,
        recruitment_end__gte=current_date
    )
    return filtered_scholarships

scholarships = filter_scholarships_by_date(current_date, scholarships)

위의 함수를 통해 사용자가 입력한 날짜(current_date)를 기준으로 장학금 모집 기간(recruitment_startrecruitment_end)을 비교하여 해당 날짜에 모집 중인 장학금만 필터링한다.

함수의 인수

  • current_date: 클라이언트로부터 POST로 받은 날짜
  • scholarships: db에 저장된 모든 장학금 데이터

반환값

  • filtered_scholarships: 주어진 날짜에 모집 중인 장학금

2. 대학구분, 학과구분, 학년구분으로 필터링

def filter_basic(scholarships, user_info):  
    # 대학구분 필터링
    filtered_by_university = scholarships.filter(
        models.Q(university_type__icontains=user_info.univ_category) |  
        models.Q(university_type__icontains='해당없음') |
        models.Q(university_type__icontains='특정대학')
    )
    # 학년구분 필터링
    filtered_by_academic_year = filtered_by_university.filter(
        models.Q(academic_year_type__icontains=user_info.semester) |  
        models.Q(academic_year_type__icontains='해당없음')
    )
    # 학과구분 필터링
    filtered_by_major = filtered_by_academic_year.filter(
        models.Q(major_field_type__icontains=user_info.major_category) |  
        models.Q(major_field_type__icontains='제한없음') |
        models.Q(major_field_type__icontains='특정학과')
    )
    return filtered_by_major
 
scholarships = filter_basic(scholarships, user_profile)

위의 함수를 통해 사용자 프로필 정보(user_info)를 기준으로 대학구분, 학년구분, 학과구분 조건에 따라 장학금을 필터링한다.

대학구분, 학년구분, 학과구분에서 특정 조건이 없으면 ‘제한없음’이나 ‘해당없음’이라고 적혀있기 때문에 해당 장학금들은 필터링되지 않도록 했다.

대학구분 필터링

  • 사용자가 입력한 대학구분(user_info.univ_category)과 장학금의 university_type이 일치하는지 확인

학년구분 필터링

  • 사용자가 입력한 학년(user_info.semester)과 장학금의 academic_year_type이 일치하는지 확인

학과구분 필터링

  • 사용자가 입력한 학과구분(user_info.major_category)과 장학금의 major_field_type이 일치하는지 확인

university_type이 '특정대학'인 경우, 나머지 조건을 뒤의 추가 필터링 단계(6번) 단계)에서 처리하기 위해 필터링하지 않고 남겨뒀다.

함수의 인수

  • user_info: 사용자 프로필 정보
  • scholarships: 위의 1번에서 필터링 된 후 남은 장학금 데이터

3. 거주 지역 조건이 있는 장학금과 없는 장학금으로 분리

def separate_scholarships(scholarships):
    no_criteria_scholarships = scholarships.filter(residency_requirement_details="해당없음")
    criteria_scholarships = scholarships.exclude(residency_requirement_details="해당없음")
    return no_criteria_scholarships, criteria_scholarships
   
no_criteria_scholarships, criteria_scholarships = separate_scholarships(scholarships) 

거주 지역 조건이 없는 장학금은 ‘해당 없음’이라고 적혀있으므로 위의 함수를 통해 거주 지역 조건이 있는 장학금과 없는 장학금으로 분리한다.

반환 값

  • no_criteria_scholarships: 거주 지역 조건이 없는 장학금
  • criteria_scholarships: 거주 지역 조건이 있는 장학금

4. 프롬프트 엔지니어링을 통해 사용자의 거주지역과 장학금의 거주 지역 조건 비교

def gpt_filter_region(user_info, criteria_scholarships):
    user_info_text = f"학생의 거주지역: {user_info.residence}"  
    scholarships_text = "\n".join([
        f"번호: {scholarship.product_id}, 운영기관명: {scholarship.foundation_name}, 상품명: {scholarship.name}, 지역 거주 여부: {scholarship.residency_requirement_details}, 특정 자격: {scholarship.specific_qualification_details}"
        for scholarship in scholarships
    ])

    prompt = f"""
    {user_info_text}
    아래는 현재 모집 중인 장학금 목록입니다.
    이 목록에서 학생의 거주 지역 조건과 일치하는 장학금만 반환해 주세요.
    조건에 맞지 않는 장학금은 제외해 주세요.

    주의사항:
    1. 학생의 거주지역이 명시된 장학금이 아닐 경우, 해당 장학금을 제외해 주세요.
    2. '관내 지역'이라는 표현이 있는 경우, 장학금에서 언급된 지역과 학생의 거주지역이 정확히 일치해야 합니다. 예를 들어, '의성군 관내'라면 학생이 의성군에 거주하는 경우만 해당 장학금이 적합합니다.
    3. 학생의 거주지역이 장학금의 지역 조건과 일치하지 않으면 그 장학금은 제외해 주세요.

    장학금 목록:
    {scholarships_text}
    """

    response = openai.ChatCompletion.create(
        model="gpt-4o-mini",
        messages=[{"role": "user", "content": prompt}],
        max_tokens=1000,
        temperature=0.3
    )

    remaining_scholarships_text = response['choices'][0]['message']['content'].strip()
    remaining_scholarships_ids = re.findall(r"번호: (\d+)", remaining_scholarships_text)

    return [int(scholarship_id) for scholarship_id in remaining_scholarships_ids]

filtered_ids = gpt_filter_region(user_profile, criteria_scholarships)    

위의 함수에서 사용자의 거주 지역 정보와 장학금의 거주 지역 조건을 비교하여, 조건에 맞는 장학금을 필터링한다. 이를 위해서 GPT-4o-mini 모델에 프롬프트를 전달하여 거주 지역 조건만을 기준으로 장학금을 걸러낸다.

프롬프트의 내용

  • user_info.residence에 포함된 사용자의 거주 지역 정보를 프롬프트에 명시함
  • 각 장학금의 residency_requirement_details 필드를 포함해 장학금의 거주 지역 조건을 명확히 전달
  • 사용자의 거주 지역과 맞지 않으면 제외하도록 함
  • ‘관내 지역’과 같이 GPT가 이해하기 어려운 말은 예를 들어 자세하게 작성하여 필터링을 강화함

결과

  • filtered_ids: 거주 지역 조건이 있는 장학금(criteria_scholarships)을 위의 함수를 통해 필터링되고 남은 장학금들의 고유 id → 사용자의 거주 지역과 일치하는 장학금들

5. 4번에서 얻은 지역 조건이 맞는 장학금과 지역 조건이 없는 장학금 합침

filtered_scholarships = criteria_scholarships.filter(product_id__in=filtered_ids)
final_scholarships = filtered_scholarships | no_criteria_scholarships
  • filtered_scholarships: 4번에서 필터링되고 남은 장학금들
  • no_criteria_scholarships: 지역 조건이 없는 장학금들
  • final_scholarships: 4번에서 필터링되고 남은 장학금들과 지역 조건이 없는 장학금을 합친 장학금들

6. 5번에서 합친 장학금을 프롬프트 엔지니어링을 통해 장학금의 나머지 조건들 충족하는지 비교

# 소득분위와 중위소득 비율 매핑
income_brackets = {
    '1분위': '30%',
    '2분위': '50%',
    '3분위': '70%',
    '4분위': '90%',
    '5분위': '100%',
    '6분위': '130%',
    '7분위': '150%',
    '8분위': '200%',
    '9분위': '300%',
    '10분위': '300% 초과'
}

# GPT 기반 추천 장학금 필터링
def recommend_scholarships(user_info, scholarships):
    middle_income_ratio = income_brackets.get(user_info.income, '알 수 없음')  # 필드로 접근

    user_info_text = f"""
    학생의 프로필:
    - 대학구분: {user_info.univ_category}
    - 성별: {user_info.gender}
    - 나이: {user_info.age}
    - 대학교: {user_info.university}
    - 학년: {user_info.semester}
    - 학과: {user_info.major_category}
    - 전공: {user_info.major}
    - 평균 성적: {user_info.totalGPA} (4.5점 만점)
    - 소득분위: {user_info.income} (중위소득 비율: {middle_income_ratio})
    - 거주지역: {user_info.residence}
    - 기타 정보: {user_info.etc}
    """

    scholarships_text = "\n".join([
        f"번호: {scholarship.product_id}, 운영기관명: {scholarship.foundation_name}, 상품명: {scholarship.name}, 성적 기준: {scholarship.grade_criteria_details}, 소득 기준: {scholarship.income_criteria_details}, 지역 거주 여부: {scholarship.residency_requirement_details}, 특정 자격: {scholarship.specific_qualification_details}"
        for scholarship in scholarships
    ])

    prompt = f"""
    {user_info_text}
    위 학생의 조건에 맞는 장학금만 추천해 주세요.

    1. 성적 기준: 사용자의 성적이 장학금의 성적 기준보다 높거나 '해당 없음'인 장학금만 추천하세요.
    2. 소득 기준: 사용자의 소득분위가 장학금의 소득 기준보다 낮거나 '해당 없음'인 장학금만 추천하세요.
    3. 거주지역 요건: 사용자의 거주지역과 장학금의 거주지역 기준이 일치하거나 '해당 없음'인 장학금만 추천하세요.
    4. 전공 요건: 사용자의 전공과 일치하거나 '전공 불문'인 장학금만 추천하세요.특정 전공을 요구하는 장학금에서 사용자의 전공과 맞지 않는 경우에는 제외해 주세요.
    5. 특정 자격 요건: 특정 자격(예: 특정 직업의 자녀, 특정 대회 입상자 등)을 요구하는 장학금은, 사용자가 해당 조건을 충족하지 않는 경우 제외해 주세요.
    6. 기타: 사용자의 프로필의 기타 정보에 적힌 내용과 일치하는 장학금만 추천해 주세요.

    사용자에게 적합한 장학금이면 모두 추천해 주세요. 
    **사용자와 조건이 하나라도 일치하지 않으면 제외해주세요.**
    장학금 목록:
    {scholarships_text}
    
    출력 형식은 아래와 같이 해줘
    (예시)
    1. **장학금 번호: 1,119**
    - 운영기관명:
    - 성적 기준:
    - 소득 기준:
    - 지역 거주 여부:
    - 특정 자격:

    2. **장학금 번호: 1,244**
    - 운영기관명:
    - 성적 기준:
    - 소득 기준:
    - 지역 거주 여부:
    - 특정 자격:
    """

    response = openai.ChatCompletion.create(
        model="gpt-4o-mini",
        messages=[{"role": "user", "content": prompt}],
        max_tokens=1000
    )

    recommended_scholarships_text = response['choices'][0]['message']['content'].strip()
    

위의 함수에서 5번에서 합친 장학금 데이터를 사용자 프로필 정보와 비교하여, 모든 조건을 충족하는 장학금을 필터링한다. 이를 위해 GPT-4o-mini 모델에 프롬프트를 전달하여 조건 비교를 수행한다.

성적 기준, 소득 기준, 거주지역 요건, 전공 요건, 특정 자격 요건 등 각 조건별로 장학금과 사용자 정보를 비교하여 하나라도 충족되지 않으면 해당 장학금 제외하도록 프롬프트를 작성했다. 그리고 데이터베이스에 저장할 때 장학금 id로 저장하기 떄문에 출력형식을 지정하여 반환하도록 했다.

사용자 정보

  • 사용자 프로필(user_info)을 기반으로 성적, 소득분위, 거주지역 등 정보를 문자열로 작성하여 프롬프트에 포함

장학금 정보

  • 장학금의 성적 기준, 소득 기준, 지역 요건, 특정 자격 요건 등 데이터를 문자열로 작성하여 GPT가 조건 비교를 수행하도록 함

프롬프트 내용

  • 성적 기준: 사용자의 성적이 장학금 기준보다 높거나 기준이 없으면 포함.
  • 소득 기준: 사용자의 소득분위가 장학금 기준보다 낮거나 기준이 없으면 포함.
  • 거주지역 요건: 사용자의 거주지역과 일치하거나 조건이 없으면 포함.
  • 특정 자격 요건: 특정 자격 요건이 사용자의 기타 정보와 일치하면 포함.
  • 출력 형식: 장학금 번호 및 관련 정보를 포함하여 반환.

7. 최종 추천된 장학금을 DB에 저장

        for scholarship in final_scholarships:
            RecommendResult.objects.create(
                user=user_profile.user,
                scholarship=scholarship,
                product_id=scholarship.product_id
            )

        serializer = self.get_serializer(final_scholarships, many=True)
        return Response(serializer.data, status=status.HTTP_200_OK)
  • final_scholarships: 6번까지의 과정을 통해 최종 추천된 장학금 리스트
  • RecommendResult 모델을 사용하여 최종 추천된 장학금을 사용자(user_profile.user)와 연결하여 추천된 장학금의 product_id도 함께 데이터베이스에 저장한다.

사용자1 추천 결과

1. 사용자 프로필을 다음과 같이 작성한다.

2. 추천받고 싶은 날짜를 입력한다.

3. 추천된 장학금 목록

공무원연금공단의 대여학자금

  • 사용자가 기타에 작성한 공무원 자녀라는 점을 바탕으로 “현직 공무원 본인 및 자녀의 대학 등록금”이 자격인 장학금을 추천해주었다.

사용자2 추천 결과

1. 사용자 프로필을 다음과 같이 작성한다.

2. 추천받고 싶은 날짜를 입력한다.

3. 추천된 장학금 목록

한국산림복지진흥원의 녹색학업장학생

  • 사용자의 학과가 “산림조경과”이며 기타에 “산림관련 공모전 수상” 경험이 있다는 점을 바탕으로 “산림전공자”가 자격인 장학금을 추천해주었다.

제주특별자치도청의 학자금대출이자지원


  • 사용자의 거주지역이 “제주특별시”라는 점에서 제주특별자치도에 거주해야 하는 지역연고 장학금을 추천해주었다.

결론

약 2000개의 장학금 데이터를 GPT에 바로 보내면 성능이 떨어지므로 먼저 필터링하여 장학금 개수를 줄이고, GPT를 통해 추천받도록 했다.

0개의 댓글