線上上廣告系統中,精準投放是提升使用者經驗和廣告效益的關鍵。本文將介紹如何使用 Redis 建立一個根據內容和地理位置匹配的廣告投放系統。系統的核心流程包含內容分析、地理位置匹配、廣告排序和結果記錄。首先,系統會分析使用者瀏覽內容並提取關鍵字,接著根據使用者地理位置篩選廣告,再根據廣告的 eCPM 值進行排序,最後記錄投放結果以供後續分析和最佳化。系統利用 Redis 的高效能和資料結構特性,例如 ZSET 和 SET,來儲存和管理廣告、關鍵字和地理位置資訊,以實作快速匹配和排序。程式碼範例中展示瞭如何使用 Redis pipeline 提高效率,並透過 union 和 zintersect 等指令進行集合運算。

第7章:根據搜尋的應用程式 - 廣告投放系統的實作

在現代線上廣告系統中,精準的廣告投放是提升使用者經驗和廣告成效的關鍵。本章將探討如何利用Redis實作一個根據內容匹配的廣告投放系統。我們將詳細分析系統的設計思路、技術實作以及關鍵程式碼。

廣告投放流程概述

廣告投放的核心是根據使用者的瀏覽內容和地理位置,精準地選擇最合適的廣告進行投放。整個流程包括以下幾個主要步驟:

  1. 內容分析:對使用者的瀏覽內容進行分析,提取關鍵字。
  2. 地理位置匹配:根據使用者的地理位置篩選合適的廣告。
  3. 廣告排序:根據廣告的eCPM(有效每千人成本)進行排序,選擇最優廣告。
  4. 結果記錄:記錄廣告投放結果,用於後續的資料分析和最佳化。

關鍵技術實作

地理位置匹配

地理位置匹配是廣告投放的第一步。我們需要根據使用者的地理位置(城市、州、國家)來篩選合適的廣告。以下程式碼展示瞭如何實作這一功能:

def match_location(pipe, locations):
    required = ['req:' + loc for loc in locations]
    matched_ads = union(pipe, required, ttl=300, _execute=False)
    return matched_ads, zintersect(pipe,
                                   {matched_ads:0, 'ad:value:':1}, _execute=False)

內容解密:

此函式接收兩個引數:pipe(Redis管道物件)和locations(使用者的地理位置列表)。函式首先構建一個包含所有地理位置鍵名的列表required,然後利用union函式計算出符合這些地理位置的廣告集合matched_ads。接著,利用zintersect函式計算這些廣告的基本eCPM值。值得注意的是,_execute=False引數使得這些操作被暫存於管道中,延遲執行,以減少與Redis的通訊次數。

計算廣告投放獎勵

廣告投放獎勵的計算是根據內容匹配的。我們需要根據廣告中的關鍵字與使用者瀏覽內容的匹配程度來調整廣告的eCPM。以下程式碼展示瞭如何實作這一功能:

def finish_scoring(pipe, matched, base, content):
    bonus_ecpm = {}
    words = tokenize(content)
    for word in words:
        word_bonus = zintersect(
            pipe, {matched:0, word:1}, _execute=False)
        bonus_ecpm[word_bonus] =1
    if bonus_ecpm:
        minimum = zunion(
            pipe, bonus_ecpm, aggregate='MIN', _execute=False)
        maximum = zunion(
            pipe, bonus_ecpm, aggregate='MAX', _execute=False)

內容解密:

此函式負責計算廣告的最終eCPM值。首先,對使用者的瀏覽內容進行分詞處理,提取關鍵字。然後,對於每個關鍵字,計算其與廣告的匹配程度,並將結果存入bonus_ecpm字典中。接著,分別計算這些匹配程度的最小值和最大值。最終,透過對最小值和最大值的平均,得到廣告的最終eCPM獎勵值。

系統最佳化與未來發展

本系統透過Redis有效地實作了廣告的精準投放,但在實際應用中,還需要考慮更多的最佳化措施,例如:

  1. 資料分析:透過記錄廣告投放結果,不斷最佳化廣告選擇演算法。
  2. 實時處理:利用Redis的實時資料處理能力,提升廣告投放的時效性。
  3. 多維度匹配:除了地理位置和內容匹配外,考慮引入更多的匹配維度,如使用者行為、廣告型別等。

透過這些最佳化措施,可以進一步提升廣告投放的精準度和系統的整體效能。

圖表說明

  graph LR
    A[開始] --> B[內容分析]
    B --> C[地理位置匹配]
    C --> D[廣告排序]
    D --> E[結果記錄]
    E --> F[結束]

圖表翻譯:

此圖示展示了廣告投放系統的主要流程。首先,系統對使用者的瀏覽內容進行分析。接著,根據使用者的地理位置篩選合適的廣告。然後,根據廣告的eCPM進行排序,選擇最優廣告。最後,記錄廣告投放結果,用於後續的資料分析和最佳化。整個流程確保了廣告投放的精準性和時效性。

第7章:根據搜尋的應用程式 - 廣告定向技術

在前面的章節中,我們討論瞭如何使用Redis進行廣告定向。本章節將探討廣告定向的技術細節,包括如何根據網頁內容進行廣告匹配,以及如何從使用者行為中學習以最佳化廣告定向。

7.3 廣告定向技術詳解

廣告定向是根據使用者的地理位置、網頁內容等資訊,選擇最合適的廣告進行展示的過程。在我們的例子中,我們使用了Redis的ZSET資料結構來儲存廣告的定向資訊。

7.3.1 定向廣告的實作

定向廣告的實作主要涉及以下幾個步驟:

  1. 計算廣告的基礎eCPM:eCPM(每千次展示成本)是廣告定向的一個重要指標。我們首先計算廣告的基礎eCPM。

  2. 根據地理位置匹配廣告:我們使用Redis的ZSET來儲存與特定地理位置相關的廣告ID及其對應的分數。

  3. 根據網頁內容匹配廣告:我們提取網頁內容中的關鍵字,並將這些關鍵字與廣告進行匹配。匹配成功的廣告將獲得額外的分數加成。

  4. 綜合計算廣告的分數:我們綜合廣告的基礎eCPM、地理位置匹配分數以及網頁內容匹配分數,計算出最終的eCPM。

以下是一個簡化的程式碼範例,展示瞭如何實作定向廣告:

def target_ads(conn, user_id, page):
    # 提取網頁內容中的關鍵字
    words = extract_words_from_page(page)
    
    # 取得使用者的地理位置
    location = get_user_location(user_id)
    
    # 根據地理位置匹配廣告
    location_ads = conn.zrange('location:%s' % location, 0, -1)
    
    # 計算廣告的分數
    scores = {}
    for ad_id in location_ads:
        base_score = conn.hget('ad:%s' % ad_id, 'ecpm')
        word_bonus = calculate_word_bonus(conn, ad_id, words)
        scores[ad_id] = base_score + word_bonus
    
    # 傳回分數最高的廣告
    return max(scores, key=scores.get)

def calculate_word_bonus(conn, ad_id, words):
    # 取得廣告相關的關鍵字
    ad_words = conn.smembers('terms:%s' % ad_id)
    
    # 計算匹配的關鍵字的分數
    bonus = 0
    for word in words & ad_words:
        bonus += conn.zscore('word:%s' % word, ad_id)
    
    return bonus

#### 內容解密:

上述程式碼展示瞭如何根據使用者的地理位置和網頁內容進行廣告定向。首先,我們提取網頁內容中的關鍵字,並取得使用者的地理位置。然後,我們根據地理位置匹配廣告,並計算每個廣告的分數。最後,我們傳回分數最高的廣告。

target_ads函式中,我們首先提取網頁內容中的關鍵字,並取得使用者的地理位置。然後,我們使用Redis的ZSET來取得與使用者地理位置相關的廣告ID。接著,我們計算每個廣告的分數,包括基礎eCPM和根據網頁內容匹配的關鍵字的分數加成。最後,我們傳回分數最高的廣告。

calculate_word_bonus函式中,我們取得廣告相關的關鍵字,並計算匹配的關鍵字的分數。我們使用Redis的SET來儲存廣告相關的關鍵字,並使用ZSET來儲存關鍵字與廣告ID的對應關係。

7.3.4 從使用者行為中學習

為了最佳化廣告定向,我們需要從使用者行為中學習。使用者行為包括使用者是否點選了廣告、使用者是否對廣告進行了互動等。我們可以根據這些行為資料,調整廣告的分數,從而最佳化廣告定向。

以下是一個簡化的程式碼範例,展示瞭如何記錄使用者行為並更新廣告的分數:

def record_targeting_result(conn, target_id, ad_id, words):
    pipeline = conn.pipeline(True)
    terms = conn.smembers('terms:' + ad_id)
    matched = list(words & terms)
    if matched:
        matched_key = 'terms:matched:%s' % target_id
        pipeline.sadd(matched_key, *matched)
        pipeline.expire(matched_key, 900)
    type = conn.hget('type:', ad_id)
    pipeline.incr('type:%s:views:' % type)
    for word in matched:
        pipeline.zincrby('views:%s' % ad_id, word)
    pipeline.zincrby('views:%s' % ad_id, '')
    if not pipeline.execute()[-1] % 100:
        update_cpms(conn, ad_id)

def update_cpms(conn, ad_id):
    # 取得廣告的點選率和轉化率
    clicks = conn.zscore('clicks:%s' % ad_id, '')
    views = conn.zscore('views:%s' % ad_id, '')
    # 更新廣告的eCPM
    ecpm = calculate_ecpm(clicks, views)
    conn.hset('ad:%s' % ad_id, 'ecpm', ecpm)

#### 內容解密:

上述程式碼展示瞭如何記錄使用者行為並更新廣告的分數。在record_targeting_result函式中,我們記錄了使用者行為資料,包括廣告的展示次數和點選次數。然後,我們根據這些資料,更新廣告的eCPM。

update_cpms函式中,我們取得廣告的點選率和轉化率,並根據這些資料,計算廣告的eCPM。然後,我們更新廣告的eCPM值。

廣告點選與動作記錄的實作與最佳化

在前面的章節中,我們詳細探討了廣告定向功能的實作以及其在廣告系統中的重要性。現在,我們將進一步深入瞭解廣告點選和動作記錄的實作細節,以及如何利用這些資料來最佳化廣告的eCPM(每千次展示的預期收入)。

記錄點選和動作的必要性

要計算廣告的點選率(CTR),我們需要記錄兩類別資料:一是廣告的展示次數,二是廣告的點選或動作次數。對於CPA(按動作付費)廣告,我們尤其需要記錄使用者的動作。這些資料對於計算廣告的eCPM至關重要,因為eCPM的計算公式為:(點選或動作的價值) x (點選或動作次數) / 展示次數。如果不記錄點選和動作,分子將為0,從而無法進行有意義的計算。

實作點選和動作記錄的功能

下面是記錄點選的函式實作:

def record_click(conn, target_id, ad_id, action=False):
    pipeline = conn.pipeline(True)
    click_key = 'clicks:%s' % ad_id
    match_key = 'terms:matched:%s' % target_id
    type = conn.hget('type:', ad_id)
    
    if type == 'cpa':
        pipeline.expire(match_key, 900)  # 重新整理CPA廣告匹配詞的過期時間
    if action:
        click_key = 'actions:%s' % ad_id  # 記錄動作而非點選
    
    if action and type == 'cpa':
        pipeline.incr('type:cpa:actions:' + type)  # 記錄CPA廣告的動作次數
    pipeline.incr('type:%s:clicks:' % type)  # 記錄廣告型別的點選次數
    
    matched = list(conn.smembers(match_key))  # 取得匹配的詞
    matched.append('')  # 新增空字串以記錄廣告本身的點選
    
    for word in matched:
        pipeline.zincrby(click_key, word)  # 為每個匹配詞增加點選/動作次數
    
    pipeline.execute()
    update_cpms(conn, ad_id)  # 更新廣告的eCPM

內容解密:

  1. record_click函式:該函式負責記錄廣告的點選或動作,並更新相關的統計資料。它首先初始化一個Redis事務Pipeline,以確保資料的一致性。
  2. 廣告型別判斷:根據廣告的型別(CPA或CPC),決定如何處理點選或動作的記錄。對於CPA廣告,函式會重新整理匹配詞的過期時間,以確保動作可以在點選後的一段時間內被記錄。
  3. 點選/動作記錄:函式將點選或動作的次數記錄到對應的ZSET中,包括廣告的總點選/動作次數和每個匹配詞的點選/動作次數。
  4. 更新eCPM:最後,函式呼叫update_cpms來更新廣告的eCPM。

更新eCPM的實作

更新eCPM的過程涉及計算廣告的點選率和每個匹配詞對廣告eCPM的貢獻。具體步驟如下:

  1. 計算廣告的點選率:透過廣告的點選次數和展示次數計算廣告的點選率。
  2. 計算廣告的eCPM:結合廣告的點選率和其價值,計算廣告的eCPM。
  3. 計算匹配詞的eCPM貢獻:對於每個匹配詞,計算其對廣告eCPM的貢獻,並將這個貢獻值更新到對應的ZSET中。
def update_cpms(conn, ad_id):
    pipeline = conn.pipeline(True)
    # ... 省略了具體的eCPM計算和更新邏輯
    pipeline.execute()

內容解密:

  1. update_cpms函式:該函式負責更新廣告的eCPM和匹配詞的eCPM貢獻。
  2. eCPM計算:函式透過廣告的點選率和價值計算其eCPM,並進一步計算每個匹配詞對廣告eCPM的貢獻。
  3. 資料更新:計算出的eCPM和貢獻值被更新到Redis的相應ZSET中,以便後續的廣告排序和選擇。

練習:改變點選和動作的計數方式

record_click函式中,我們為每個匹配詞增加了1次點選/動作。思考一下,是否可以根據匹配詞的數量調整這個增量?例如,可以將增量設為1除以匹配詞的數量,以平均分配點選/動作的價值。

實作提示:

  1. 修改record_click函式:計算匹配詞的數量,並據此調整ZINCRBY命令的增量。
  2. 同步更新finish_scoring函式:如果有必要,更新該函式以配合新的計數方式。

透過這樣的修改,可以更精確地評估每個匹配詞對廣告表現的貢獻,從而最佳化廣告的定向和排序。