주요 컨텐츠로 이동
Engineering blog

Translated by HaUn Kim - Original Blog Post

마케팅과 광고 지출이 어떻게 매출 증가에 연결될 수 있는지 궁금하신가요? 광고 환경이 지속적으로 변화함에 따라, 미디어 믹스 내에서 다양한 수익 창출 마케팅 활동의 영향력을 효율적으로 파악하는 것은 광고주들에게 점점 더 큰 부담이 되고 있습니다.

브랜드들은 매년 수십억 달러를 제품 홍보에 투자합니다. 이런 마케팅 지출은 3~6개월 전에 계획되어, 인지도 제고, 체험단 설립, 브랜드 제품 및 서비스의 소비 증가를 위한 프로모션 전략을 추진하는 데 사용됩니다. 그러나 이 전체 모델은 COVID-19로 인해 큰 혼란을 겪고 있습니다. 소비자 행동은 빠르게 변화하고 있고, 브랜드들은 더 이상 몇 달 전에 프로모션 지출을 미리 계획할 수 있는 여유가 없습니다. 브랜드들은 몇 주 혹은 몇 일, 심지어는 거의 실시간으로 결정을 내려야 합니다. 따라서, 브랜드들은 디지털 광고와 프로모션 같은 더 민첩한 채널로 예산을 재분배하고 있습니다.

이런 변화에 대응하는 것은 쉽지 않습니다. 디지털 전략은 개별 소비자가 가장 공감할 가능성이 높은 메시지를 전달함으로써 개인화를 강화할 수 있는 중요한 전략이지만, 기존의 통계 분석 및 미디어 플래닝 도구는 집계 데이터를 사용하고 리드 타임이 길기 때문에 세그먼트 또는 개인 수준에서 메시지를 최적화하기란 쉽지 않습니다. 마케팅 또는 미디어 믹스 모델링(MMM)은 일반적으로 다양한 마케팅 전략의 영향을 이해하고 향후 이니셔티브에 대한 최적의 지출 수준을 결정하는 데 사용되지만, MMM은 광범위한 데이터 세트를 통합해야 하는 복잡성으로 인해 매우 수동적이고 시간 집약적이며 후향적인 (backwards-looking) 작업입니다.

인쇄 및 TV 광고 대행사는 격주 단위의 엑셀 스프레드시트를 통해 지정 시장 영역(DMA) 수준의 노출을 제공하고, 디지털 대행사는 우편번호 수준의 클릭 및 노출을 보여주는 CSV 파일을 제공합니다. 판매 데이터는 시장 수준에서 받아볼 수 있으며, 검색과 소셜은 각각 다양한 요인으로 잠재고객을 분류하는 API를 통해 고유한 보고서를 제공할 수 있습니다. 브랜드가 디지털 미디어와 더 민첩한 광고 방식으로 전환함에 따라, 신속하게 통합하고 분석해야 하는 이질적인 데이터 세트의 수가 증가하고 있습니다. 이로 인해 이러한 다양한 데이터 소스를 통합하고 오버레이하는 데 몇 주 혹은 몇 달이 걸릴 수 있는 MMM 과정을 대부분의 마케터는 분기별로 한 번(대부분 연간으로 한 번)에만 수행하게 됩니다.

MMM은 광범위한 수준의 마케팅 투자 결정에 유용하지만, 브랜드는 보다 세분화된 수준에서 신속하게 결정을 내릴 수 있는 능력이 필요합니다. 새로운 마케팅 데이터를 통합하고 분석을 수행하며, 의사 결정을 몇 달 혹은 몇 주에서 며칠 혹은 몇 시간으로 단축할 수 있어야 합니다. 실시간으로 대응할 수 있는 브랜드는 그 노력의 결과로 훨씬 높은 투자 수익률을 볼 수 있습니다.

매출 예측 및 광고 어트리뷰션 대시보드 솔루션 액셀러레이터를 소개합니다.

주요 브랜드와의 협업을 통해 얻은 모범 사례를 바탕으로, 데이터 엔지니어와 데이터 사이언티스트가 개발 시간을 몇 주 혹은 몇 달 단축할 수 있도록 일반적인 분석 및 머신러닝 사용 사례를 위한 솔루션 액셀러레이터를 개발하였습니다.

광고 대행사이든 사내 마케팅 분석팀이든, 이 솔루션 액셀러레이터를 사용하면 다양한 과거 및 현재 소스의 판매, 광고 참여, 지역 데이터를 쉽게 연결하여 이러한 데이터가 지역 수준에서 어떻게 매출을 촉진하는지 확인할 수 있습니다. 이 솔루션을 통해 Apple의 IDFA 사용 중단 소식으로 인해 증대된 쿠키/기기 ID 추적 및 매핑에 대한 우려 없이도, 종합적인 트렌드 수준에서 디지털 마케팅 활동의 어트리뷰션을 할 수 있습니다.

일반적으로 어트리뷰션은 상당히 비용이 많이 드는 프로세스이며, 특히 적절한 기술 없이 지속적으로 업데이트되는 데이터 세트에 대해 어트리뷰션을 수행하는 경우에는 더욱 그렇습니다. 하지만 다행히도, 데이터브릭스에서는 클라우드 데이터 레이크를 관리하기 위한 오픈 소스 트랜잭션 레이어인 델타 레이크를 포함한 통합 데이터 분석 플랫폼을 제공하여, 멀티 클라우드 인프라에서 대규모 데이터 엔지니어링 및 데이터 과학을 수행할 수 있도록 지원합니다. 이 글에서는 데이터브릭스가 어떻게 다단계 델타 레이크 변환, 머신러닝, 캠페인 데이터 시각화를 통해 실행 가능한 인사이트를 제공하는지 알려드리겠습니다.

이 솔루션 액셀러레이터는 다음의 세 가지 특징으로 다른 광고 어트리뷰션 도구들과 차별화됩니다:

  1. 통합성이 용이한 새로운 데이터 소스 기능: 델타 아키텍처의 한 가지 장점은 신규 데이터를 스키마에 원활하게 통합할 수 있다는 것입니다. 예를 들어, 델타 레이크의 자동화된 데이터 보강 기능을 통해 다양한 데이터와 다른 시간/날짜 형식을 가진 신규 데이터 소스를 쉽게 통합할 수 있습니다. 이로 인해 마케팅 전략을 모델에 편리하게 오버레이하고, 새로운 데이터 소스를 선명하게 통합할 수 있습니다.
  2. 실시간 대시보드: 대다수의 MMM은 특정 시점의 분석에 초점을 맞추지만, 액셀러레이터의 자동화된 데이터 파이프라인은 비즈니스 사용자에게 파일 생성 즉시 광고 노출에서 매출까지를 매핑하거나 예측할 수 있는 대시보드를 제공합니다. 이를 통해 일일 또는 세그먼트 수준의 데이터 시각화를 쉽게 얻을 수 있습니다.
  3. 머신러닝과의 통합: 이 솔루션의 머신러닝 모델을 이용하면, 마케팅 데이터 팀은 일별 또는 개인별 수준에서 특정 광고가 어떤 고객 세그먼트에 어떻게 반응하는지에 대한 보다 세밀한 하향식 또는 상향식 분석을 수행할 수 있습니다.

데이터브릭스의 델타 레이크는 모든 마케팅 데이터에 대한 구조와 스키마를 제공함으로써, BI 및 AI 팀의 데이터 사용의 중심이 되어, 마케팅 데이터 레이크를 효과적으로 구축하는 데 기여합니다.

이 솔루션이 기존의 MMM, 예측, 어트리뷰션을 확장하고 개선하는 방법

이 솔루션의 가장 큰 장점 두 가지는 신뢰할 수 있는 데이터 수집 및 준비와 민첩한 데이터 분석, 그리고 머신러닝 작업이 결합된 통합 인사이트 플랫폼을 통해 기존의 MMM, 예측 및 어트리뷰션보다 인사이트 도출 시간을 단축하고 세분화를 높인다는 것입니다. 마케터는 MMM을 활용하여 캠페인 지출을 최적화하는 결정을 내릴 때, 기존에는 수동적인 방식으로 장기 미디어 구매 데이터를 수집하고 프로모션, 경쟁사, 브랜드 자산, 계절성이나 경제적 요소 등 캠페인에 영향을 미칠 수 있는 거시적 요소들을 관찰해 왔습니다. 일반적으로 MMM 주기는 몇 주 또는 몇 달이 소요되며, 캠페인이 시작된 후 오랜 시간이 지나야 실행 가능한 인사이트를 제공하거나, 캠페인이 끝날 때까지도 인사이트를 제공하지 못하는 경우가 많습니다. 이런 상황에서는 캠페인을 최대한 효과적으로 진행하기 위한 중요 인사이트와 핵심 요소를 활용하기엔 너무 늦은 경우가 많습니다.

게다가, MMM은 더 세분화된 수준에서 최적의 메시지를 고려하지 않는 한편, 하향식 인사이트만 제공하며 미디어 믹스 전략을 추천하는 데 초점을 맞추는 경향이 있습니다. 광고 활동이 디지털 미디어로 크게 이동하면서 기존의 MMM 접근법은 이런 사용자 수준의 기회를 효과적으로 활용할 수 있는 방법에 대한 인사이트를 제공하지 못하게 되었습니다.

광고 데이터의 수집, 처리, 분석, 그리고 데이터 사이언스를 단일 플랫폼으로 통합하면, 마케팅 데이터 팀은 하향식 및 상향식, 세분화된 수준에서 인사이트를 생성할 수 있습니다. 이를 통해 마케터는 일 단위 또는 사용자 단위의 심층 분석을 즉시 수행하고, 마케팅 믹스에서 광고주의 노력이 가장 큰 영향을 미치는 위치를 정확하게 파악하여 적절한 채널을 통해 적시에 적절한 메시지를 최적화할 수 있습니다. 결국, 마케터는 더욱 효율적이고 통합된 측정 접근 방식을 통해 엄청난 이점을 얻을 수 있습니다.

솔루션 개요

Architecture overview for the Databricks Sales Forecasting and Advertising Attribution Dashboard Solution Accelerator.

우리는 상위 수준에서 지역 매출의 시계열을 지난 30일 동안의 지역 오프라인 및 온라인 광고 노출과 연결하고 있습니다. ML을 이용하여 각 지역에서 다양한 유형의 측정치(예: TV 노출 수 또는 GRP, 디지털 배너 클릭 수, 소셜 미디어 '좋아요' 수 등)를 비교한 뒤, 참여 유형을 지역별 매출 증가와 연관시키는 어트리뷰션 및 예측 모델을 구축합니다. 여기서의 문제점은 서로 다른 스키마를 가진 여러 데이터 소스의 광고 KPI(노출 수, 클릭 수, 페이지 뷰 등)를 통합하는 것입니다(예: 한 소스에서는 일 단위로 노출 수를 측정하고 다른 소스에서는 정확한 시간과 날짜를 사용하거나, 한 소스에서는 우편번호로 위치를 측정하고 다른 소스에서는 대도시 지역별로 위치를 측정할 수 있습니다).

예를 들어, 우리는 같은 체인의 레스토랑에 대한 유동인구 데이터를 얻기 위해 SafeGraph의 리치 데이터 세트를 사용하고 있습니다. 이 예에서는 가상의 오프라인 매장 방문 데이터를 사용하고 있지만, 판매 데이터에 지역과 날짜가 포함되어 있다면 오프라인 및 온라인 판매 데이터를 쉽게 연결할 수 있습니다. 여러 위치의 매장 방문 데이터를 가져와서 PySpark와 Spark SQL에서 데이터를 조사하고, 깨끗하고 신뢰할 수 있는, 그리고 ML 작업을 위해 준비된 분석용 데이터를 생성합니다. 이 예제에서 마케팅 팀은 매장 방문을 유도하는 데 가장 효과적인 온라인 미디어 채널을 찾고자 합니다.

주요 단계는 다음과 같습니다:

  1. 수집: SafeGraph 형식의 월별 유동인구 시계열을 가상 생성 - 여기서는 스키마에 맞게 데이터를 가상 생성했습니다(브론즈 단계).
  2. Feature engineering: 월별 시계열 데이터를 변환하여 날짜별 방문 횟수(행 = 날짜)에 대한 숫자 값을 일치시킵니다(실버 단계).
  3. 데이터 보강: 지역별 캠페인 데이터를 지역별 매출 위에 오버레이합니다. 분포 확인 및 변수 변환 등의 기능에 대한 탐색적 분석을 수행합니다(골드 단계).
  4. 고급 분석/머신 러닝: 예측 및 어트리뷰션 모델을 구축합니다.

1. 델타 형식을 이용한 데이터 수집 (브론즈 단계)

“Campaign Effectiveness_Forecasting Foot Traffic_ETL” 노트북으로 시작합니다.

첫 번째 단계는 블롭 스토리지에서 데이터를 로드하는 것입니다. 현재 많은 광고주들이 캠페인 데이터를 블롭 스토리지로 수집하는 방식을 선택하고 있습니다. 예를 들어, FBX Facebook 광고 인사이트 API를 통해 프로그래밍 방식으로 데이터를 검색할 수 있습니다. 이를 통해 노출 수, CTR, CPC 등을 쿼리할 수 있습니다. 대부분의 경우, 데이터는 CSV 또는 XLS 형식으로 반환됩니다. 이 예제에서는 소스 파일 디렉터리가 설정되면, 블롭에서 데이터브릭으로 원시 CSV 파일을 직접 로드할 수 있도록 S3 버킷을 dbfs에 미리 마운트합니다.

    raw_sim_ft = spark.read.format("csv").option("header", "true").option("sep", ",").load("/tmp/altdata_poi/foot_traffic.csv")
    raw_sim_ft.createOrReplaceTempView("safegraph_sim_foot_traffic")    

그런 다음 임시 보기를 생성하여 Spark SQL을 사용하여 해당 파일과 직접 상호 작용할 수 있습니다. 현재로서는 매장 방문 일별 데이터가 방대하기 때문에 나중에 기능 엔지니어링 작업을 해야 할 것입니다. 이 시점에서 저는 델타 형식을 사용하여 데이터를 작성하여 델타 레이크 브론즈 테이블을 만들어 블롭 위치를 가리키는 모든 원시 데이터를 캡처할 준비가 되었습니다. 브론즈 테이블은 다양한 소스에서 일괄 처리 또는 스트리밍을 통해 원시 데이터가 지속적으로 들어오는 데이터 레이크의 첫 번째 정류장 역할을 하며, 데이터를 원래의 원시 형식으로 캡처하고 저장할 수 있는 곳입니다. 이 단계의 데이터는 다양한 소스에서 가져오기 때문에 더티 데이터가 될 수 있습니다.

raw_sim_ft.write.format('delta').mode('overwrite').save('/home/layla/data/table/footTrafficBronze')

2. 판매 시계열을 플로팅 가능한 상태로 만들기 위한 기능 엔지니어링(실버 단계)

원시 데이터를 가져온 후에는 이제 몇 가지 데이터 정리 및 기능 엔지니어링 작업을 수행해야 합니다. 예를 들어, MSA 지역을 추가하고 월/연도를 구문 분석하는 것입니다. 그리고 visit_by_date가 배열이라는 것을 알았으므로 데이터를 별도의 행으로 분해해야 합니다. 이 함수 블록은 배열을 평평하게 만듭니다. 이 함수를 실행하면 각 행에 매핑된 num_visit과 함께 방문_by_day df가 반환됩니다:

    def parser(element):
        return json.loads(element)
    def parser_maptype(element):
        return json.loads(element, MapType(StringType(), IntegerType()))
    jsonudf = udf(parser, MapType(StringType(), IntegerType()))
    convert_array_to_dict_udf = udf(lambda arr: {idx: x for idx, x in enumerate(json.loads(arr))}, MapType(StringType(), IntegerType()))
    
    def explode_json_column_with_labels(df_parsed, column_to_explode, key_col="key", value_col="value"):
        df_exploded = df_parsed.select("safegraph_place_id", "location_name", "msa", "date_range_start", "year", "month", "date_range_end", explode(column_to_explode)).selectExpr("safegraph_place_id", "date_range_end", "location_name","msa", "date_range_start", "year", "month", "key as {0}".format(key_col), "value as {0}".format(value_col))
        return(df_exploded)

기능 엔지니어링이 끝나면 다운스트림 비즈니스 팀에서 데이터를 사용할 준비가 된 것입니다. 팀원 모두가 데이터에 직접 액세스할 수 있도록 데이터를 델타 레이크 실버 테이블에 보존할 수 있습니다. 이 단계에서는 데이터가 깨끗해지며 여러 다운스트림 골드 테이블이 이 데이터에 의존하게 됩니다. 비즈니스 팀마다 추가 데이터 변환을 위한 자체 비즈니스 로직이 있을 수 있습니다. 예를 들어, 여기에는 인사이트 대시보드 채우기, 메트릭 집합을 사용한 보고서 생성 또는 ML 알고리즘에 피드백과 같이 매우 다른 목적을 가진 여러 다운스트림 테이블에 수화되는 '분석용 기능'이라는 실버 테이블이 있다고 상상할 수 있습니다.

3. 광고 캠페인 오버레이를 통한 데이터 보강(골드 - 분석 준비 완료)

Data enrichment with advertising campaign overlay (Gold - Analytics Ready)

이 단계에서는 온라인 캠페인 미디어 데이터를 활용하여 데이터 세트를 보강할 준비가 되었습니다. 기존의 MMM 데이터 수집 단계에서는 데이터 보강 서비스가 일반적으로 분석 플랫폼에 도달하기 전에 데이터 레이크 외부에서 이루어집니다. 두 접근 방식 모두 비즈니스의 기본 광고 데이터(예: 노출, 클릭, 전환, 잠재고객 속성)를 인구통계학적, 지리적, 심리학적, 구매 행동에 대한 보다 완전한 그림으로 변환한다는 목표는 동일합니다.

데이터 보강은 일회성 프로세스가 아닙니다. 잠재고객의 위치, 선호도, 행동과 같은 정보는 시간이 지남에 따라 변화합니다. 델타레이크를 활용하면 광고 데이터와 오디언스 프로필을 지속적으로 업데이트하여 데이터를 깨끗하고 관련성 있으며 유용한 상태로 유지할 수 있습니다. 마케터와 데이터 분석가는 고객과 함께 진화하는 보다 완벽한 소비자 프로필을 구축할 수 있습니다. 이 예에서는 배너 노출 수, 소셜 미디어 FB 좋아요 수, 웹 랜딩 페이지 방문 수 등이 있습니다. Spark SQL을 사용하면 다양한 데이터 스트림을 원본 데이터 프레임에 매우 쉽게 조인할 수 있습니다. 데이터를 더욱 풍부하게 하기 위해 Google 트렌드 API를 호출하여 자연 검색 요소를 나타내는 Google 트렌드 키워드 검색 인덱스를 가져옵니다(이 데이터는 Google 트렌드에서 가져온 것입니다).

    def parser(element):x
        return json.loads(element)
    def parser_maptype(element):
        return json.loads(element, MapType(StringType(), IntegerType()))
    
    jsonudf = udf(parser, MapType(StringType(), IntegerType()))
    
    convert_array_to_dict_udf = udf(lambda arr: {idx: x for idx, x in enumerate(json.loads(arr))}, MapType(StringType(), IntegerType()))
    
    def explode_json_column_with_labels(df_parsed, column_to_explode, key_col="key", value_col="value"):
        df_exploded = df_parsed.select("safegraph_place_id", "location_name", "msa", "date_range_start", "year", "month", "date_range_end", explode(column_to_explode)).selectExpr("safegraph_place_id", "date_range_end", "location_name","msa", "date_range_start", "year", "month", "key as {0}".format(key_col), "value as {0}".format(value_col))
    
        return(df_exploded)

With the Databricks marketing mix analysis solution, you can quickly generate insights by plotting time-series graphs to, for example, visualize trends in counts or numerical values over time.

마지막으로, 매장 내 num_visit과 온라인 미디어 데이터를 결합한 데이터 세트가 생성됩니다. num_visit 시계열을 플로팅하여 인사이트를 빠르게 도출할 수 있습니다. 예를 들어 그래프를 사용하여 시간 경과에 따른 횟수 또는 수치 추세를 시각화할 수 있습니다. 이 경우 날짜 및 시간 정보는 연속적인 집계 카운트 데이터이므로 점들이 X축을 따라 그려지고 연속적인 선으로 연결됩니다. 누락된 데이터는 파선으로 표시됩니다.

시계열 그래프는 다음과 같은 데이터에 대한 질문에 답할 수 있습니다: 시간이 지남에 따라 추세가 어떻게 변하는가? 또는 누락된 값이 있는가? 아래 그래프는 2019년 1월부터 2020년 2월까지 기간의 매장 내 방문을 보여줍니다. 매장 방문이 가장 많았던 시기는 2019년 9월 중순이었습니다. 해당 기간에 마케팅 캠페인이 진행되었다면 캠페인이 효과적이었지만 제한된 기간 동안만 진행되었다는 것을 의미합니다.

Time-series visualizations produced by the Databricks marketing mix analysis solution give at-a-glance answers to questions like “how do trends change over time?” or “what data is missing?”

이 깨끗하고 풍부한 데이터 집합을 델타 레이크에 작성하고 그 위에 골드 테이블을 만듭니다.

4. 고급 분석 및 머신 러닝을 통한 예측 및 어트리뷰션 모델 구축

기존의 MMM은 분산 분석(ANOVA)과 다중 회귀를 조합하여 사용합니다. 이 솔루션에서는 두 번째 ML 노트북의 모델 설명자 SHAP에 기본으로 제공되는 장점을 가진 ML 알고리즘 XGBoost를 사용하는 방법을 시연합니다. 이 솔루션이 기존 MMM 프로세스를 대체하지 않더라도, 기존 MMM 통계학자는 단일 노드 코드를 작성하고 pandas_udf를 사용하여 실행할 수 있습니다.

다음 단계에서는 “Campaign Effectiveness_Forecasting Foot Traffic_Machine Learning" 노트북을 사용합니다.

여기까지는 모든 원시 데이터를 수집하고 결합하기 위해 데이터브릭을 사용한 다음, 더 빠른 쿼리 성능을 위해 데이터를 정리, 변환하고 Delta Lake에 기록하여 데이터의 안정성을 더했습니다.

이 시점에서 데이터 집합에 대해 꽤 만족스러워해야 합니다. 이제 어트리뷰션 모델을 만들 차례입니다. 선별된 골드 테이블 데이터를 사용하여 뉴욕시의 유동인구를 면밀히 살펴보고 패스트푸드 체인의 다양한 광고 캠페인 활동이 매장 방문을 어떻게 유도했는지 이해해 보겠습니다.

주요 단계는 다음과 같습니다:

  1. 일련의 온라인 미디어 데이터가 주어지면 매장 방문 횟수를 예측하는 머신 러닝 접근 방식을 만듭니다.
  2. SHAP 모델 인터프리터를 활용하여 모델 예측을 분해하고 특정 미디어 채널이 얼마나 많은 방문 트래픽을 유도했는지 정량화합니다.

데이터 사이언스의 표준 단계로서, 대상 변수인 매장 방문의 확률 분포와 잠재적 특징을 이해하고자 하는데, 이는 모집단의 기본 특성을 암시하는 데이터의 가능한 모든 값(또는 구간)을 알려주기 때문입니다. 이 차트에서 모든 뉴욕주 매장 방문에 대해 다중 모드 분포를 나타내는 2개의 피크가 있음을 빠르게 확인할 수 있습니다. 이는 여러 세그먼트에서 인구의 근본적인 차이를 의미하며, 이를 더 자세히 분석해야 합니다.

    %sql
    select * from 
    (select region, city, cast(year as integer) year, cast(month as integer) month, cast(day as integer) day, sum(num_visits) num_visits 
    from layla_v2.Subway_foot_traffic 
    where region = 'NY' and num_visits >= 50
    group by region, city, cast(year as integer), cast(month as integer), cast(day as integer)
    order by year, month, day, num_visits
    )

Databricks’ marketing mix analysis solution uses attribution models to help advertisers understand probability distributions and answer questions like how or why one city’s foot traffic differs from others.

뉴욕시 트래픽을 다른 모든 도시와 분리하면 분포가 정상에 가깝게 나타나는데, 이는 뉴욕시가 독특한 지역임을 의미합니다.

Databricks’ marketing mix analytics solution allows the advertiser to drill down into a dataset and isolate and draw conclusions from, for example, foot traffic specific to NYC.

다음으로 Q-Q 플롯과 정규성 테스트를 사용하여 모든 피처의 분포를 확인합니다. 차트를 보면 피처들이 상당히 정규분포를 따르고 있음을 알 수 있습니다. 여기에는 아름다운 종 모양의 곡선이 나타납니다.

The Databricks marketing mix analytics solution allows the data science team to check the distribution for all features using Q-Q Plots and normality tests.

데이터브릭스에서 분석할 때의 주요 장점 중 하나는 Spark 데이터프레임을 판다스로 자유롭게 전환할 수 있다는 점이며, 이를 통해 노트북에서 데이터를 탐색하고 plotly와 같은 인기 있는 시각화 라이브러리를 사용하여 차트를 그릴 수 있습니다. 다음 차트는 plotly에서 가져온 것입니다. 확대, 축소, 디테일한 드릴다운을 통해 모든 데이터 요소를 세밀하게 살펴볼 수 있습니다.

확대/축소 패널이 있는 플롯형 차트

보시다시피, 동일한 노트북 환경에서도 필요한 모든 통계 플롯을 매우 쉽게 생성할 수 있습니다.

이제 데이터가 모델 학습에 적합하다는 확신이 듭니다. 이제 예측 모델을 훈련시켜 볼까요? 알고리즘 선택은 XGBoost를 사용하겠습니다. 데이터 세트 크기가 크지 않아 단일 노드 학습이 효과적인 접근법이 될 것입니다. 훈련 데이터 크기가 메모리에 맞는 경우, 최적의 하이퍼파라미터를 찾는 효율성을 높이기 위해 HyperOpt를 사용하여 단일 머신에서 ML 모델의 하이퍼파라미터를 조정하는 것이 좋습니다(예: 모델의 하이퍼파라미터를 조정하기 위해):

 

    from hyperopt import fmin, tpe, rand, hp, Trials, STATUS_OK
    import xgboost
    from xgboost import XGBRegressor
    from sklearn.model_selection import cross_val_score
    import mlflow
    import mlflow.xgboost
    
    from sklearn.model_selection import train_test_split
    pdf = city_pdf.copy()
    X_train, X_test, y_train, y_test = train_test_split(pdf.drop(['region',	'year',	'month','day','date', 'num_visits'], axis=1), pdf['num_visits'], test_size=0.33, random_state=55)
    
    def train(params):
        """
        An example train method that computes the square of the input.
        This method will be passed to `hyperopt.fmin()`.
        
        :param params: hyperparameters. Its structure is consistent with how search space is defined. See below.
        :return: dict with fields 'loss' (scalar loss) and 'status' (success/failure status of run)
        """
        curr_model =  XGBRegressor(learning_rate=params[0],
                                gamma=int(params[1]),
                                max_depth=int(params[2]),
                                n_estimators=int(params[3]),
                                min_child_weight = params[4], objective='reg:squarederror')
        score = -cross_val_score(curr_model, X_train, y_train, scoring='neg_mean_squared_error').mean()
        score = np.array(score)
        
        return {'loss': score, 'status': STATUS_OK, 'model': curr_model}
    
    
    # define search parameters and whether discrete or continuous
    search_space = [ hp.uniform('learning_rate', 0, 1),
                        hp.uniform('gamma', 0, 5),
                        hp.randint('max_depth', 10),
                        hp.randint('n_estimators', 20),
                        hp.randint('min_child_weight', 10)
                    ]
    # define the search algorithm (TPE or Randomized Search)
    algo= tpe.suggest
    
    from hyperopt import SparkTrials
    search_parallelism = 4
    spark_trials = SparkTrials(parallelism=search_parallelism)
    
    with mlflow.start_run():
        argmin = fmin(
        fn=train,
        space=search_space,
        algo=algo,
        max_evals=8,
        trials=spark_trials)
    
    
    def fit_best_model(X, y): 
        client = mlflow.tracking.MlflowClient()
        experiment_id = client.get_experiment_by_name(experiment_name).experiment_id
    
        runs = mlflow.search_runs(experiment_id)
        best_loss = runs['metrics.loss'].min()
        best_run=runs[runs['metrics.loss'] == best_loss]
    
        best_params = {}
        best_params['gamma'] = float(best_run['params.gamma'])
        best_params['learning_rate'] = float(best_run['params.learning_rate'])
        best_params['max_depth'] = float(best_run['params.max_depth'])
        best_params['min_child_weight'] = float(best_run['params.min_child_weight'])  
        best_params['n_estimators'] = float(best_run['params.n_estimators'])
        
        xgb_regressor =  XGBRegressor(learning_rate=best_params['learning_rate'],
                                max_depth=int(best_params['max_depth']),
                                n_estimators=int(best_params['n_estimators']),
                                gamma=int(best_params['gamma']),
                                min_child_weight = best_params['min_child_weight'], objective='reg:squarederror')
    
        xgb_model = xgb_regressor.fit(X, y, verbose=False)
    
        return(xgb_model)
    
    # fit model using best parameters and log the model
    xgb_model = fit_best_model(X_train, y_train) 
    mlflow.xgboost.log_model(xgb_model, "xgboost") # log the model here 
    
    
    from sklearn.metrics import r2_score
    from sklearn.metrics import mean_squared_error
    train_pred = xgb_model.predict(X_train)
    test_pred = xgb_model.predict(X_test)

With Databricks’ marketing mix analytics solution, one can leverage its Runtime AutoML capabilities (HyperOp) to distributedly tune the model’s hyper parameters so that we can increase the efficiency of finding the best hyperparameters.

Spark_trials를 지정하면 HyperOpt가 자동으로 Apache Spark 클러스터 전체에 튜닝 작업을 배포합니다. HyperOpt가 최적의 파라미터 집합을 찾은 후에는, 모델을 한 번만 적합시키면 최적의 모델을 얻을 수 있습니다. 이는 수백 번의 모델 적합을 반복하고 교차 검증을 통해 최적의 모델을 찾는 것보다 훨씬 효율적입니다. 이제 적합한 모델을 사용해 뉴욕시 매장 내 트래픽을 예측해 봅시다:

    def fit_best_model(X, y): 
    client = mlflow.tracking.MlflowClient()
    experiment_id = client.get_experiment_by_name(experiment_name).experiment_id
    
    runs = mlflow.search_runs(experiment_id)
    best_loss = runs['metrics.loss'].min()
    best_run=runs[runs['metrics.loss'] == best_loss]
    
    best_params = {}
    best_params['gamma'] = float(best_run['params.gamma'])
    best_params['learning_rate'] = float(best_run['params.learning_rate'])
    best_params['max_depth'] = float(best_run['params.max_depth'])
    best_params['min_child_weight'] = float(best_run['params.min_child_weight'])  
    best_params['n_estimators'] = float(best_run['params.n_estimators'])
    
    xgb_regressor =  XGBRegressor(learning_rate=best_params['learning_rate'],
                                max_depth=int(best_params['max_depth']),
                                n_estimators=int(best_params['n_estimators']),
                                gamma=int(best_params['gamma']),
                                min_child_weight = best_params['min_child_weight'], objective='reg:squarederror')
    
    xgb_model = xgb_regressor.fit(X, y, verbose=False)
    
    return(xgb_model)
    
    # fit model using best parameters and log the model
    xgb_model = fit_best_model(X_train, y_train) 
    mlflow.xgboost.log_model(xgb_model, "xgboost") # log the model here 
    
    from sklearn.metrics import r2_score
    from sklearn.metrics import mean_squared_error
    train_pred = xgb_model.predict(X_train)
    test_pred = xgb_model.predict(X_test)
  

With Databricks’ marketing mix analytics solution, one can use HyperOpt to automatically distribute a tuning job across a Spark cluster to, for example, find the optimal model to forecast intore traffic for a specific city.

빨간색 선은 예측을 나타내고 파란색은 실제 방문 횟수를 나타냅니다. 여기저기서 몇 가지 급격한 증가를 놓쳤지만, 모델이 주요 추세를 잘 포착한 것으로 보입니다. 약간의 조정이 필요할 수 있지만, 짧은 시간 동안 이정도 결과를 얻은 것은 나쁘지 않은 결과인 것 같습니다.

예측 모델이 완성되면 이 모델이 어떻게 예측을 하는지 자연스럽게 궁금해집니다. 각 기능이 이 블랙박스 알고리즘에 어떻게 기여하는 것일까요? 우리의 경우에는 "각 미디어 입력이 매장 내 유동인구에 얼마나 기여하는가"라는 질문이 생깁니다.

OSS 모델 인터프리터인 SHAP 라이브러리를 활용하면 "오프라인 활동을 유도하는 가장 중요한 미디어 채널은 무엇인가?"와 같은 인사이트를 신속하게 도출할 수 있습니다.

SHAP를 사용하면 몇 가지 이점이 있습니다. 첫째, 개별 입력 수준에서 설명을 생성할 수 있습니다. 기존의 특징 중요도 알고리즘은 전체 모집단에서 가장 중요한 특징을 제시하지만, 각 개별 관찰에는 고유한 SHAP 값 집합이 있습니다. 그러나 전체 수준에서는 이러한 개별적인 변화가 사라지고 가장 일반적인 패턴만 남게 됩니다. 개별 수준의 SHAP 값을 사용하면 각 관찰에 가장 큰 영향을 미치는 요인을 정확히 파악할 수 있으므로, 모델의 결과를 더욱 견고하게 만들고 인사이트를 더욱 실현 가능하게 만들 수 있습니다. 따라서 우리의 경우, 매일 매장 방문에 대한 각 미디어 입력에 대한 SHAP 값을 계산합니다.

    shap.summary_plot(shap_values, X, plot_type="bar")

With Databricks’ marketing mix analytics solution, one can use the SHAP library to quickly identify, for example, which social media and landing page visits had the highest contribution to the model.

    display(spark.createDataFrame(sorted(list(zip(mean_abs_shap, X.columns)), reverse=True)[:8], ["Mean |SHAP|", "Feature"]))

With Databricks’ marketing mix analytics solution, one can use the SHAP library to quickly derive insights such as “what are the most important media channels driving my offline activities?”

SHAP는 기능 값을 출력 단위와 직접 연결시켜 미디어 믹스 기여도에 대한 세분화된 인사이트를 제공할 수 있습니다. 여기서 SHAP는 피처가 모델 목표의 단위인 매장 내 방문에 미치는 영향을 정량화할 수 있습니다. 이 그래프에서 우리는 방문 단위로 피처의 영향을 읽을 수 있어, 피처 중요도에 따른 상대 점수에 비해 결과의 해석이 크게 향상됩니다.

    plot_html = shap.force_plot(explainer.expected_value, shap_values[n:n+1], feature_names=X.columns, plot_cmap='GnPR')  
    displayHTML(bundle_js + plot_html.data)

With Databricks’ marketing mix analytics solution, one can use the SHAP library to quantify, for example, the impact of a feature on the unit of model target, the in-store visit.

마지막으로, 일일 유동인구 시계열에 대한 전체 분해 차트를 생성하여 매장 방문이 각 온라인 미디어 입력에 어떻게 기여하는지 명확하게 파악할 수 있습니다. 이전에는 데이터 사이언티스트가 복잡한 변환과 앞뒤 계산을 수반하는 분해 행렬을 작성해야 했습니다. SHAP를 사용하면 즉시 가치를 얻을 수 있습니다!

    import plotly.graph_objects as go
    
    fig = go.Figure(data=[
        go.Bar(name='base_value', x=shap_values_pdf['date'], y=shap_values_pdf['base_value'], marker_color='lightblue'),
        go.Bar(name='banner_imp', x=shap_values_pdf['date'], y=shap_values_pdf['banner_imp']),
        go.Bar(name='social_media_like', x=shap_values_pdf['date'], y=shap_values_pdf['social_media_like']),
        go.Bar(name='landing_page_visit', x=shap_values_pdf['date'], y=shap_values_pdf['landing_page_visit']),
        go.Bar(name='google_trend', x=shap_values_pdf['date'], y=shap_values_pdf['google_trend'])
    ])
    # Change the bar mode
    fig.update_layout(barmode='stack')
    fig.show()

확대/축소 패널이 있는 플롯형 차트

분석의 기반이 되는 코드를 다운로드하여 검토할 수 있도록 공개하고 있습니다. 이 솔루션을 사용자 환경에 배포하는 방법에 대해 궁금한 점이 있으면 주저하지 말고 문의해 주세요.

노트북 받기

Databricks 무료로 시작하기

관련 포스트

Engineering blog

매출 예측 및 어트리뷰션으로 광고 효과 측정하기

Translated by HaUn Kim - Original Blog Post 노트북을 다운로드하고, 이 솔루션 액셀러레이터에 관한 웨비나를 확인해 보세요. 마케팅과 광고 지출이 어떻게 매출 증가에...
모든 엔지니어링 블로그 포스트 보기