ad_recommend.py 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751
  1. import json
  2. import time
  3. import traceback
  4. import datetime
  5. from utils import RedisHelper
  6. from config import set_config
  7. from log import Log
  8. from lr_model import get_final_score
  9. log_ = Log()
  10. config_ = set_config()
  11. redis_helper = RedisHelper()
  12. def get_params(ab_exp_info, ab_test_code):
  13. """
  14. 根据实验分组给定对应的参数
  15. :param ab_exp_info: AB实验组参数
  16. :param ab_test_code: 用户对应的ab组
  17. :return:
  18. """
  19. abtest_id, abtest_config_tag = None, None
  20. ad_abtest_id_list = [key.split('-')[0] for key in config_.AD_ABTEST_CONFIG]
  21. # 获取广告实验配置
  22. config_value_dict = {}
  23. if ab_exp_info:
  24. ab_exp_list = ab_exp_info.get('ab_test002', None)
  25. if ab_exp_list:
  26. for ab_item in ab_exp_list:
  27. ab_exp_code = ab_item.get('abExpCode', None)
  28. if not ab_exp_code:
  29. continue
  30. if ab_exp_code in ad_abtest_id_list:
  31. config_value = ab_item.get('configValue', None)
  32. if config_value:
  33. config_value_dict[str(ab_exp_code)] = eval(str(config_value))
  34. if len(config_value_dict) > 0:
  35. for ab_exp_code, config_value in config_value_dict.items():
  36. for tag, value in config_value.items():
  37. if ab_test_code in value:
  38. abtest_id = ab_exp_code
  39. abtest_config_tag = tag
  40. break
  41. return abtest_id, abtest_config_tag
  42. def get_threshold(abtest_id, abtest_config_tag, ab_test_code, mid_group, care_model_status, abtest_param):
  43. """获取对应的阈值"""
  44. # 判断是否是关怀模式实验
  45. care_model_status_param = abtest_param.get('care_model_status_param', None)
  46. care_model_ab_mid_group = abtest_param.get('care_model_ab_mid_group', [])
  47. if care_model_status_param is None:
  48. # 无关怀模式实验
  49. threshold_key_name_prefix = config_.KEY_NAME_PREFIX_AD_THRESHOLD
  50. else:
  51. # 关怀模式实验
  52. if care_model_status is None or len(care_model_ab_mid_group) == 0 or care_model_status == 'null':
  53. # 参数缺失,走默认
  54. threshold_key_name_prefix = config_.KEY_NAME_PREFIX_AD_THRESHOLD
  55. elif int(care_model_status) == int(care_model_status_param) and mid_group in care_model_ab_mid_group:
  56. # 实验匹配,获取对应的阈值
  57. threshold_key_name_prefix = config_.KEY_NAME_PREFIX_AD_THRESHOLD_CARE_MODEL
  58. else:
  59. threshold_key_name_prefix = config_.KEY_NAME_PREFIX_AD_THRESHOLD
  60. threshold_key_name = f"{threshold_key_name_prefix}{abtest_id}:{abtest_config_tag}:{ab_test_code}:{mid_group}"
  61. threshold = redis_helper.get_data_from_redis(key_name=threshold_key_name)
  62. if threshold is None:
  63. threshold = 0
  64. else:
  65. threshold = float(threshold)
  66. return threshold
  67. def predict_with_rate_process(now_date, video_id, abtest_param, abtest_id, abtest_config_tag, ab_test_code, care_model_status, mid_group):
  68. now_dt = datetime.datetime.strftime(now_date, '%Y%m%d')
  69. user_data_key = abtest_param['user'].get('data')
  70. user_rule_key = abtest_param['user'].get('rule')
  71. video_data_key = abtest_param['video'].get('data')
  72. # 获取用户组分享率
  73. group_share_rate_key = f"{config_.KEY_NAME_PREFIX_AD_GROUP}{user_data_key}:{user_rule_key}:{now_dt}"
  74. if not redis_helper.key_exists(group_share_rate_key):
  75. redis_dt = datetime.datetime.strftime(now_date - datetime.timedelta(days=1), '%Y%m%d')
  76. group_share_rate_key = f"{config_.KEY_NAME_PREFIX_AD_GROUP}{user_data_key}:{user_rule_key}:{redis_dt}"
  77. group_share_rate = redis_helper.get_score_with_value(key_name=group_share_rate_key, value=mid_group)
  78. # 获取视频分享率
  79. video_share_rate_key = f"{config_.KEY_NAME_PREFIX_AD_VIDEO}{video_data_key}:{now_dt}"
  80. if not redis_helper.key_exists(video_share_rate_key):
  81. redis_dt = datetime.datetime.strftime(now_date - datetime.timedelta(days=1), '%Y%m%d')
  82. video_share_rate_key = f"{config_.KEY_NAME_PREFIX_AD_VIDEO}{video_data_key}:{redis_dt}"
  83. video_share_rate = redis_helper.get_score_with_value(key_name=video_share_rate_key, value=int(video_id))
  84. if video_share_rate is None:
  85. video_share_rate = redis_helper.get_score_with_value(key_name=video_share_rate_key, value=-1)
  86. # 计算 mid-video 分享率
  87. if group_share_rate is None or video_share_rate is None:
  88. return None
  89. mid_video_predict_res = float(group_share_rate) * float(video_share_rate)
  90. # 获取对应的阈值
  91. threshold = get_threshold(
  92. abtest_id=abtest_id,
  93. abtest_config_tag=abtest_config_tag,
  94. ab_test_code=ab_test_code,
  95. mid_group=mid_group,
  96. care_model_status=care_model_status,
  97. abtest_param=abtest_param
  98. )
  99. # 阈值判断
  100. if mid_video_predict_res > threshold:
  101. # 大于阈值,出广告
  102. ad_predict = 2
  103. else:
  104. # 否则,不出广告
  105. ad_predict = 1
  106. result = {
  107. 'mid_group': mid_group,
  108. 'group_share_rate': group_share_rate,
  109. 'video_share_rate': video_share_rate,
  110. 'mid_video_predict_res': mid_video_predict_res,
  111. 'threshold': threshold,
  112. 'ad_predict': ad_predict
  113. }
  114. return result
  115. def predict_mid_video_res(now_date, mid, video_id, abtest_param, abtest_id, abtest_config_tag, ab_test_code, care_model_status, app_type):
  116. # now_dt = datetime.datetime.strftime(now_date, '%Y%m%d')
  117. # user_data_key = abtest_param['user'].get('data')
  118. # user_rule_key = abtest_param['user'].get('rule')
  119. # video_data_key = abtest_param['video'].get('data')
  120. group_class_key = abtest_param.get('group_class_key')
  121. no_ad_mid_group_list = abtest_param.get('no_ad_mid_group_list', [])
  122. no_ad_group_with_video_mapping = abtest_param.get('no_ad_group_with_video_mapping', {})
  123. # 判断mid所属分组
  124. mid_group_key_name = f"{config_.KEY_NAME_PREFIX_MID_GROUP}{group_class_key}:{mid}"
  125. mid_group = redis_helper.get_data_from_redis(key_name=mid_group_key_name)
  126. if mid_group is None:
  127. mid_group = 'mean_group'
  128. # 判断用户是否在免广告用户组列表中
  129. if mid_group in no_ad_mid_group_list:
  130. # 在免广告用户组列表中,则不出广告
  131. ad_predict = 1
  132. result = {
  133. 'mid_group': mid_group,
  134. 'ad_predict': ad_predict,
  135. 'no_ad_strategy': 'no_ad_mid_group'
  136. }
  137. elif mid_group in no_ad_group_with_video_mapping:
  138. # 用户组在特定内容不出广告设置中
  139. # 获取对应的特定内容
  140. video_mapping_key_list = no_ad_group_with_video_mapping.get(mid_group, [])
  141. no_ad_videos = redis_helper.get_data_from_redis(key_name=f"{config_.KEY_NAME_PREFIX_NO_AD_VIDEOS}{app_type}")
  142. if no_ad_videos is not None:
  143. no_ad_videos = json.loads(no_ad_videos)
  144. else:
  145. no_ad_videos = {}
  146. no_ad_video_list = []
  147. for video_mapping_key in video_mapping_key_list:
  148. no_ad_video_list.extend(no_ad_videos.get(video_mapping_key, []))
  149. # 判断此次请求视频是否在免广告视频列表中
  150. if video_id in no_ad_video_list:
  151. # 在,则不出广告
  152. ad_predict = 1
  153. result = {
  154. 'mid_group': mid_group,
  155. 'ad_predict': ad_predict,
  156. 'no_ad_strategy': 'no_ad_mid_group_with_video'
  157. }
  158. else:
  159. result = predict_with_rate_process(
  160. now_date=now_date,
  161. video_id=video_id,
  162. abtest_param=abtest_param,
  163. abtest_id=abtest_id,
  164. abtest_config_tag=abtest_config_tag,
  165. ab_test_code=ab_test_code,
  166. care_model_status=care_model_status,
  167. mid_group=mid_group)
  168. else:
  169. result = predict_with_rate_process(
  170. now_date=now_date,
  171. video_id=video_id,
  172. abtest_param=abtest_param,
  173. abtest_id=abtest_id,
  174. abtest_config_tag=abtest_config_tag,
  175. ab_test_code=ab_test_code,
  176. care_model_status=care_model_status,
  177. mid_group=mid_group)
  178. return result
  179. def predict_mid_video_res_with_add(now_date, mid, video_id, abtest_param, abtest_id, abtest_config_tag, ab_test_code, care_model_status):
  180. now_dt = datetime.datetime.strftime(now_date, '%Y%m%d')
  181. # 判断mid所属分组
  182. group_class_key = abtest_param.get('group_class_key')
  183. mid_group_key_name = f"{config_.KEY_NAME_PREFIX_MID_GROUP}{group_class_key}:{mid}"
  184. mid_group = redis_helper.get_data_from_redis(key_name=mid_group_key_name)
  185. if mid_group is None:
  186. mid_group = 'mean_group'
  187. # 判断用户是否在免广告用户组列表中
  188. no_ad_mid_group_list = abtest_param.get('no_ad_mid_group_list', [])
  189. if mid_group in no_ad_mid_group_list:
  190. # 在免广告用户组列表中,则不出广告
  191. ad_predict = 1
  192. result = {
  193. 'mid_group': mid_group,
  194. 'ad_predict': ad_predict
  195. }
  196. else:
  197. # 获取用户组出广告后分享的概率
  198. share_user_data_key = abtest_param['share']['user'].get('data')
  199. share_user_rule_key = abtest_param['share']['user'].get('rule')
  200. group_share_rate_key = \
  201. f"{config_.KEY_NAME_PREFIX_AD_GROUP}{share_user_data_key}:{share_user_rule_key}:{now_dt}"
  202. if not redis_helper.key_exists(group_share_rate_key):
  203. redis_dt = datetime.datetime.strftime(now_date - datetime.timedelta(days=1), '%Y%m%d')
  204. group_share_rate_key = \
  205. f"{config_.KEY_NAME_PREFIX_AD_GROUP}{share_user_data_key}:{share_user_rule_key}:{redis_dt}"
  206. group_share_rate = redis_helper.get_score_with_value(key_name=group_share_rate_key, value=mid_group)
  207. # 获取视频出广告后分享的概率
  208. share_video_data_key = abtest_param['share']['video'].get('data')
  209. video_share_rate_key = f"{config_.KEY_NAME_PREFIX_AD_VIDEO}{share_video_data_key}:{now_dt}"
  210. if not redis_helper.key_exists(video_share_rate_key):
  211. redis_dt = datetime.datetime.strftime(now_date - datetime.timedelta(days=1), '%Y%m%d')
  212. video_share_rate_key = f"{config_.KEY_NAME_PREFIX_AD_VIDEO}{share_video_data_key}:{redis_dt}"
  213. video_share_rate = redis_helper.get_score_with_value(key_name=video_share_rate_key, value=int(video_id))
  214. if video_share_rate is None:
  215. video_share_rate = redis_helper.get_score_with_value(key_name=video_share_rate_key, value=-1)
  216. # 获取用户组出广告后不直接跳出的概率
  217. out_user_data_key = abtest_param['out']['user'].get('data')
  218. out_user_rule_key = abtest_param['out']['user'].get('rule')
  219. group_out_rate_key = \
  220. f"{config_.KEY_NAME_PREFIX_AD_GROUP}{out_user_data_key}:{out_user_rule_key}:{now_dt}"
  221. if not redis_helper.key_exists(group_out_rate_key):
  222. redis_dt = datetime.datetime.strftime(now_date - datetime.timedelta(days=1), '%Y%m%d')
  223. group_out_rate_key = \
  224. f"{config_.KEY_NAME_PREFIX_AD_GROUP}{out_user_data_key}:{out_user_rule_key}:{redis_dt}"
  225. group_out_rate = redis_helper.get_score_with_value(key_name=group_out_rate_key, value=mid_group)
  226. # 获取视频出广告后不直接跳出的概率
  227. out_video_data_key = abtest_param['out']['video'].get('data')
  228. video_out_rate_key = f"{config_.KEY_NAME_PREFIX_AD_VIDEO}{out_video_data_key}:{now_dt}"
  229. if not redis_helper.key_exists(video_out_rate_key):
  230. redis_dt = datetime.datetime.strftime(now_date - datetime.timedelta(days=1), '%Y%m%d')
  231. video_out_rate_key = f"{config_.KEY_NAME_PREFIX_AD_VIDEO}{out_video_data_key}:{redis_dt}"
  232. video_out_rate = redis_helper.get_score_with_value(key_name=video_out_rate_key, value=int(video_id))
  233. if video_out_rate is None:
  234. video_out_rate = redis_helper.get_score_with_value(key_name=video_out_rate_key, value=-1)
  235. # 计算 mid-video 预测值
  236. if group_share_rate is None or video_share_rate is None or group_out_rate is None or video_out_rate is None:
  237. return None
  238. # 加权融合
  239. share_weight = abtest_param['mix_param']['share_weight']
  240. out_weight = abtest_param['mix_param']['out_weight']
  241. group_rate = share_weight * float(group_share_rate) + out_weight * float(group_out_rate)
  242. video_rate = share_weight * float(video_share_rate) + out_weight * float(video_out_rate)
  243. mid_video_predict_res = group_rate * video_rate
  244. # 获取对应的阈值
  245. threshold = get_threshold(
  246. abtest_id=abtest_id,
  247. abtest_config_tag=abtest_config_tag,
  248. ab_test_code=ab_test_code,
  249. mid_group=mid_group,
  250. care_model_status=care_model_status,
  251. abtest_param=abtest_param
  252. )
  253. # 阈值判断
  254. if mid_video_predict_res > threshold:
  255. # 大于阈值,出广告
  256. ad_predict = 2
  257. else:
  258. # 否则,不出广告
  259. ad_predict = 1
  260. result = {
  261. 'mid_group': mid_group,
  262. 'group_share_rate': group_share_rate,
  263. 'video_share_rate': video_share_rate,
  264. 'group_out_rate': group_out_rate,
  265. 'video_out_rate': video_out_rate,
  266. 'group_rate': group_rate,
  267. 'video_rate': video_rate,
  268. 'mid_video_predict_res': mid_video_predict_res,
  269. 'threshold': threshold,
  270. 'ad_predict': ad_predict}
  271. return result
  272. def predict_mid_video_res_with_model(now_date, mid, video_id, abtest_param, abtest_id, abtest_config_tag, ab_test_code, care_model_status, app_type):
  273. model_key = abtest_param.get('model_key', 'ad_out_v1')
  274. user_key_name = f"{config_.KEY_NAME_PREFIX_AD_OUT_MODEL_SCORE_USER}{model_key}:{mid}"
  275. item_key_name = f"{config_.KEY_NAME_PREFIX_AD_OUT_MODEL_SCORE_ITEM}{model_key}:{video_id}"
  276. user_score = redis_helper.get_data_from_redis(key_name=user_key_name)
  277. item_score = redis_helper.get_data_from_redis(key_name=item_key_name)
  278. config_key_prefix = f"{config_.KEY_NAME_PREFIX_AD_OUT_MODEL_CONFIG}{model_key}:{abtest_id}:{abtest_config_tag}"
  279. config_key = f"{config_key_prefix}:config"
  280. config_str = redis_helper.get_data_from_redis(key_name=config_key)
  281. config = {}
  282. if config_str is not None:
  283. config = json.loads(config_str)
  284. hit_strategy = 'model'
  285. threshold = config.get('threshold', None)
  286. if user_score is not None and item_score is not None:
  287. offline_score = float(user_score) + float(item_score)
  288. else:
  289. use_backup = config.get('use_backup', False)
  290. # 如果离线分数为空 & 兜底策略开启,走兜底策略
  291. if use_backup:
  292. result = predict_mid_video_res(
  293. now_date=now_date,
  294. mid=mid,
  295. video_id=video_id,
  296. abtest_param=abtest_param,
  297. abtest_id=abtest_id,
  298. abtest_config_tag=abtest_config_tag,
  299. ab_test_code=ab_test_code,
  300. care_model_status=care_model_status,
  301. app_type=app_type
  302. )
  303. if result is not None:
  304. hit_strategy = 'backup'
  305. result['hit_strategy'] = hit_strategy
  306. return result
  307. else:
  308. if item_score is not None:
  309. offline_score = float(item_score)
  310. threshold = config.get('item_threshold', None)
  311. hit_strategy = 'item'
  312. elif user_score is not None:
  313. offline_score = float(user_score)
  314. threshold = config.get('user_threshold', None)
  315. hit_strategy = 'user'
  316. else: # item_score and user_score all None
  317. offline_score = 0.0
  318. threshold = config.get('miss_threshold', None)
  319. hit_strategy = 'miss'
  320. online_features = {
  321. 'ctx_apptype': str(app_type),
  322. 'ctx_week': time.strftime('%w', time.localtime()),
  323. 'ctx_hour': time.strftime('%H', time.localtime()),
  324. }
  325. final_score, online_score = get_final_score(online_features, offline_score)
  326. rank_score = 0.0
  327. if config.get('use_rank_score', False):
  328. rank_score_key = f"rank:score1:{video_id}"
  329. rank_score_str = redis_helper.get_data_from_redis(key_name=rank_score_key)
  330. if rank_score_str is not None:
  331. rank_score = float(rank_score_str)
  332. rank_score_bias = config.get('rank_score_bias', 0.0)
  333. merge_score = config.get('final_score_w', 1.0) * final_score + config.get('rank_score_w', 1.0) * (rank_score + rank_score_bias)
  334. if threshold is None:
  335. threshold = 0.0
  336. hit_strategy = 'error_' + hit_strategy
  337. # 跳出率阈值判断
  338. if merge_score < threshold:
  339. # 小于阈值,出广告
  340. ad_predict = 2
  341. else:
  342. # 否则,不出广告
  343. ad_predict = 1
  344. result = {
  345. 'user_score': user_score,
  346. 'item_score': item_score,
  347. 'final_score': final_score,
  348. 'merge_score': merge_score,
  349. 'rank_score': rank_score,
  350. 'rank_score_bias': rank_score_bias,
  351. 'online_score': online_score,
  352. 'threshold': threshold,
  353. 'ad_predict': ad_predict,
  354. 'online_features': online_features,
  355. 'hit_strategy': hit_strategy
  356. }
  357. return result
  358. def predict_mid_video_res_with_multiply(now_date, mid, video_id, abtest_param, abtest_id, abtest_config_tag, ab_test_code, care_model_status):
  359. now_dt = datetime.datetime.strftime(now_date, '%Y%m%d')
  360. # 判断mid所属分组
  361. group_class_key = abtest_param.get('group_class_key')
  362. mid_group_key_name = f"{config_.KEY_NAME_PREFIX_MID_GROUP}{group_class_key}:{mid}"
  363. mid_group = redis_helper.get_data_from_redis(key_name=mid_group_key_name)
  364. if mid_group is None:
  365. mid_group = 'mean_group'
  366. # 判断用户是否在免广告用户组列表中
  367. no_ad_mid_group_list = abtest_param.get('no_ad_mid_group_list', [])
  368. if mid_group in no_ad_mid_group_list:
  369. # 在免广告用户组列表中,则不出广告
  370. ad_predict = 1
  371. result = {
  372. 'mid_group': mid_group,
  373. 'ad_predict': ad_predict
  374. }
  375. else:
  376. # 获取用户组出广告后分享的概率
  377. share_user_data_key = abtest_param['share']['user'].get('data')
  378. share_user_rule_key = abtest_param['share']['user'].get('rule')
  379. group_share_rate_key = \
  380. f"{config_.KEY_NAME_PREFIX_AD_GROUP}{share_user_data_key}:{share_user_rule_key}:{now_dt}"
  381. if not redis_helper.key_exists(group_share_rate_key):
  382. redis_dt = datetime.datetime.strftime(now_date - datetime.timedelta(days=1), '%Y%m%d')
  383. group_share_rate_key = \
  384. f"{config_.KEY_NAME_PREFIX_AD_GROUP}{share_user_data_key}:{share_user_rule_key}:{redis_dt}"
  385. group_share_rate = redis_helper.get_score_with_value(key_name=group_share_rate_key, value=mid_group)
  386. # 获取视频出广告后分享的概率
  387. share_video_data_key = abtest_param['share']['video'].get('data')
  388. video_share_rate_key = f"{config_.KEY_NAME_PREFIX_AD_VIDEO}{share_video_data_key}:{now_dt}"
  389. if not redis_helper.key_exists(video_share_rate_key):
  390. redis_dt = datetime.datetime.strftime(now_date - datetime.timedelta(days=1), '%Y%m%d')
  391. video_share_rate_key = f"{config_.KEY_NAME_PREFIX_AD_VIDEO}{share_video_data_key}:{redis_dt}"
  392. video_share_rate = redis_helper.get_score_with_value(key_name=video_share_rate_key, value=int(video_id))
  393. if video_share_rate is None:
  394. video_share_rate = redis_helper.get_score_with_value(key_name=video_share_rate_key, value=-1)
  395. # 获取用户组出广告后不直接跳出的概率
  396. out_user_data_key = abtest_param['out']['user'].get('data')
  397. out_user_rule_key = abtest_param['out']['user'].get('rule')
  398. group_out_rate_key = \
  399. f"{config_.KEY_NAME_PREFIX_AD_GROUP}{out_user_data_key}:{out_user_rule_key}:{now_dt}"
  400. if not redis_helper.key_exists(group_out_rate_key):
  401. redis_dt = datetime.datetime.strftime(now_date - datetime.timedelta(days=1), '%Y%m%d')
  402. group_out_rate_key = \
  403. f"{config_.KEY_NAME_PREFIX_AD_GROUP}{out_user_data_key}:{out_user_rule_key}:{redis_dt}"
  404. group_out_rate = redis_helper.get_score_with_value(key_name=group_out_rate_key, value=mid_group)
  405. # 获取视频出广告后不直接跳出的概率
  406. out_video_data_key = abtest_param['out']['video'].get('data')
  407. video_out_rate_key = f"{config_.KEY_NAME_PREFIX_AD_VIDEO}{out_video_data_key}:{now_dt}"
  408. if not redis_helper.key_exists(video_out_rate_key):
  409. redis_dt = datetime.datetime.strftime(now_date - datetime.timedelta(days=1), '%Y%m%d')
  410. video_out_rate_key = f"{config_.KEY_NAME_PREFIX_AD_VIDEO}{out_video_data_key}:{redis_dt}"
  411. video_out_rate = redis_helper.get_score_with_value(key_name=video_out_rate_key, value=int(video_id))
  412. if video_out_rate is None:
  413. video_out_rate = redis_helper.get_score_with_value(key_name=video_out_rate_key, value=-1)
  414. # 计算 mid-video 预测值
  415. if group_share_rate is None or video_share_rate is None or group_out_rate is None or video_out_rate is None:
  416. return None
  417. # 乘积融合
  418. group_rate = float(group_share_rate) * float(group_out_rate)
  419. video_rate = float(video_share_rate) * float(video_out_rate)
  420. mid_video_predict_res = group_rate * video_rate
  421. # 获取对应的阈值
  422. threshold = get_threshold(
  423. abtest_id=abtest_id,
  424. abtest_config_tag=abtest_config_tag,
  425. ab_test_code=ab_test_code,
  426. mid_group=mid_group,
  427. care_model_status=care_model_status,
  428. abtest_param=abtest_param
  429. )
  430. # 阈值判断
  431. if mid_video_predict_res > threshold:
  432. # 大于阈值,出广告
  433. ad_predict = 2
  434. else:
  435. # 否则,不出广告
  436. ad_predict = 1
  437. result = {
  438. 'mid_group': mid_group,
  439. 'group_share_rate': group_share_rate,
  440. 'video_share_rate': video_share_rate,
  441. 'group_out_rate': group_out_rate,
  442. 'video_out_rate': video_out_rate,
  443. 'group_rate': group_rate,
  444. 'video_rate': video_rate,
  445. 'mid_video_predict_res': mid_video_predict_res,
  446. 'threshold': threshold,
  447. 'ad_predict': ad_predict}
  448. return result
  449. def ad_recommend_predict(app_type, mid, video_id, ab_exp_info, ab_test_code, care_model_status):
  450. """
  451. 广告推荐预测
  452. :param app_type: app_type
  453. :param mid: mid
  454. :param video_id: video_id
  455. :param ab_exp_info: AB实验组参数
  456. :param ab_test_code: 用户对应的ab组
  457. :param care_model_status: 用户关怀模式状态 1-未开启,2-开启
  458. :return: ad_predict, type-int, 1-不发放广告,2-发放广告
  459. """
  460. try:
  461. now_date = datetime.datetime.today()
  462. # now_dt = datetime.datetime.strftime(now_date, '%Y%m%d')
  463. now_h = datetime.datetime.now().hour
  464. if 0 <= now_h < 8:
  465. # 00:00 - 08:00 不出广告
  466. ad_predict = 1
  467. result = {
  468. 'now_h': now_h,
  469. 'ad_predict': ad_predict
  470. }
  471. return result
  472. # 获取实验参数
  473. abtest_id, abtest_config_tag = get_params(ab_exp_info=ab_exp_info, ab_test_code=ab_test_code)
  474. if abtest_id is None or abtest_config_tag is None:
  475. return None
  476. abtest_param = config_.AD_ABTEST_CONFIG.get(f'{abtest_id}-{abtest_config_tag}')
  477. if abtest_param is None:
  478. return None
  479. threshold_mix_func = abtest_param.get('threshold_mix_func', None)
  480. if threshold_mix_func == 'add':
  481. result = predict_mid_video_res_with_add(
  482. now_date=now_date,
  483. mid=mid,
  484. video_id=video_id,
  485. abtest_param=abtest_param,
  486. abtest_id=abtest_id,
  487. abtest_config_tag=abtest_config_tag,
  488. ab_test_code=ab_test_code,
  489. care_model_status=care_model_status
  490. )
  491. elif threshold_mix_func == 'multiply':
  492. result = predict_mid_video_res_with_multiply(
  493. now_date=now_date,
  494. mid=mid,
  495. video_id=video_id,
  496. abtest_param=abtest_param,
  497. abtest_id=abtest_id,
  498. abtest_config_tag=abtest_config_tag,
  499. ab_test_code=ab_test_code,
  500. care_model_status=care_model_status
  501. )
  502. elif threshold_mix_func == 'model':
  503. result = predict_mid_video_res_with_model(
  504. now_date=now_date,
  505. mid=mid,
  506. video_id=video_id,
  507. abtest_param=abtest_param,
  508. abtest_id=abtest_id,
  509. abtest_config_tag=abtest_config_tag,
  510. ab_test_code=ab_test_code,
  511. care_model_status=care_model_status,
  512. app_type=app_type
  513. )
  514. else:
  515. result = predict_mid_video_res(
  516. now_date=now_date,
  517. mid=mid,
  518. video_id=video_id,
  519. abtest_param=abtest_param,
  520. abtest_id=abtest_id,
  521. abtest_config_tag=abtest_config_tag,
  522. ab_test_code=ab_test_code,
  523. care_model_status=care_model_status,
  524. app_type=app_type
  525. )
  526. # user_data_key = abtest_param['user'].get('data')
  527. # user_rule_key = abtest_param['user'].get('rule')
  528. # video_data_key = abtest_param['video'].get('data')
  529. # group_class_key = abtest_param.get('group_class_key')
  530. # no_ad_mid_group_list = abtest_param.get('no_ad_mid_group_list', [])
  531. #
  532. # # 判断mid所属分组
  533. # mid_group_key_name = f"{config_.KEY_NAME_PREFIX_MID_GROUP}{group_class_key}:{mid}"
  534. # mid_group = redis_helper.get_data_from_redis(key_name=mid_group_key_name)
  535. # if mid_group is None:
  536. # mid_group = 'mean_group'
  537. #
  538. # # 判断用户是否在免广告用户组列表中
  539. # if mid_group in no_ad_mid_group_list:
  540. # # 在免广告用户组列表中,则不出广告
  541. # ad_predict = 1
  542. # result = {
  543. # 'mid_group': mid_group,
  544. # 'ad_predict': ad_predict
  545. # }
  546. # else:
  547. # # 获取用户组分享率
  548. # group_share_rate_key = f"{config_.KEY_NAME_PREFIX_AD_GROUP}{user_data_key}:{user_rule_key}:{now_dt}"
  549. # if not redis_helper.key_exists(group_share_rate_key):
  550. # redis_dt = datetime.datetime.strftime(now_date - datetime.timedelta(days=1), '%Y%m%d')
  551. # group_share_rate_key = f"{config_.KEY_NAME_PREFIX_AD_GROUP}{user_data_key}:{user_rule_key}:{redis_dt}"
  552. # group_share_rate = redis_helper.get_score_with_value(key_name=group_share_rate_key, value=mid_group)
  553. # # 获取视频分享率
  554. # video_share_rate_key = f"{config_.KEY_NAME_PREFIX_AD_VIDEO}{video_data_key}:{now_dt}"
  555. # if not redis_helper.key_exists(video_share_rate_key):
  556. # redis_dt = datetime.datetime.strftime(now_date - datetime.timedelta(days=1), '%Y%m%d')
  557. # video_share_rate_key = f"{config_.KEY_NAME_PREFIX_AD_VIDEO}{video_data_key}:{redis_dt}"
  558. # video_share_rate = redis_helper.get_score_with_value(key_name=video_share_rate_key, value=int(video_id))
  559. # if video_share_rate is None:
  560. # video_share_rate = redis_helper.get_score_with_value(key_name=video_share_rate_key, value=-1)
  561. #
  562. # # 计算 mid-video 分享率
  563. # if group_share_rate is None or video_share_rate is None:
  564. # return None
  565. # mid_video_share_rate = float(group_share_rate) * float(video_share_rate)
  566. #
  567. # # 获取对应的阈值
  568. # threshold = get_threshold(
  569. # abtest_id=abtest_id,
  570. # abtest_config_tag=abtest_config_tag,
  571. # ab_test_code=ab_test_code,
  572. # mid_group=mid_group,
  573. # care_model_status=care_model_status,
  574. # abtest_param=abtest_param
  575. # )
  576. # # 阈值判断
  577. # if mid_video_share_rate > threshold:
  578. # # 大于阈值,出广告
  579. # ad_predict = 2
  580. # else:
  581. # # 否则,不出广告
  582. # ad_predict = 1
  583. # result = {
  584. # 'mid_group': mid_group,
  585. # 'group_share_rate': group_share_rate,
  586. # 'video_share_rate': video_share_rate,
  587. # 'mid_video_share_rate': mid_video_share_rate,
  588. # 'threshold': threshold,
  589. # 'ad_predict': ad_predict}
  590. return result
  591. except Exception as e:
  592. log_.error(traceback.format_exc())
  593. return None
  594. def ad_recommend_predict_with_roi(app_type, mid, video_id, ads, arpu, roi_param):
  595. """
  596. 广告推荐预测
  597. :param app_type: app_type
  598. :param mid: mid
  599. :param video_id: video_id
  600. :param ads: 需要发放广告列表 list
  601. :param arpu: 上一周期arpu值
  602. :param roi_param: 计算roi使用参数
  603. :return: ad_predict, type-int, 1-不发放广告,2-发放广告
  604. """
  605. try:
  606. now_date = datetime.datetime.today()
  607. now_dt = datetime.datetime.strftime(now_date, '%Y%m%d')
  608. ad_info = ads[0]
  609. ad_id = ad_info['adId']
  610. ad_type = ad_info['adType']
  611. ecpm = float(ad_info['ecpm'])
  612. # 获取参数
  613. params = config_.PARAMS_NEW_STRATEGY[int(app_type)]
  614. # 判断mid所属分组
  615. group_class_key = params.get('group_class_key')
  616. mid_group_key_name = f"{config_.KEY_NAME_PREFIX_MID_GROUP}{group_class_key}:{mid}"
  617. mid_group = redis_helper.get_data_from_redis(key_name=mid_group_key_name)
  618. if mid_group is None:
  619. mid_group = 'mean_group'
  620. # 获取用户组出广告后分享的概率
  621. share_user_data_key = params['user'].get('data')
  622. share_user_rule_key = params['user'].get('rule')
  623. group_share_rate_key_with_ad = \
  624. f"{config_.KEY_NAME_PREFIX_GROUP_WITH_AD}{share_user_data_key}:{share_user_rule_key}:{now_dt}"
  625. if not redis_helper.key_exists(group_share_rate_key_with_ad):
  626. redis_dt = datetime.datetime.strftime(now_date - datetime.timedelta(days=1), '%Y%m%d')
  627. group_share_rate_key_with_ad = \
  628. f"{config_.KEY_NAME_PREFIX_GROUP_WITH_AD}{share_user_data_key}:{share_user_rule_key}:{redis_dt}"
  629. group_share_rate_with_ad = redis_helper.get_score_with_value(key_name=group_share_rate_key_with_ad,
  630. value=mid_group)
  631. # 获取视频出广告后分享的概率
  632. share_video_data_key = params['video'].get('data')
  633. video_share_rate_key_with_ad = f"{config_.KEY_NAME_PREFIX_VIDEO_WITH_AD}{share_video_data_key}:{now_dt}"
  634. if not redis_helper.key_exists(video_share_rate_key_with_ad):
  635. redis_dt = datetime.datetime.strftime(now_date - datetime.timedelta(days=1), '%Y%m%d')
  636. video_share_rate_key_with_ad = f"{config_.KEY_NAME_PREFIX_VIDEO_WITH_AD}{share_video_data_key}:{redis_dt}"
  637. video_share_rate_with_ad = redis_helper.get_score_with_value(key_name=video_share_rate_key_with_ad,
  638. value=int(video_id))
  639. if video_share_rate_with_ad is None:
  640. video_share_rate_with_ad = redis_helper.get_score_with_value(key_name=video_share_rate_key_with_ad,
  641. value=-1)
  642. # 获取用户组不出广告后分享的概率
  643. group_share_rate_key_no_ad = \
  644. f"{config_.KEY_NAME_PREFIX_GROUP_NO_AD}{share_user_data_key}:{share_user_rule_key}:{now_dt}"
  645. if not redis_helper.key_exists(group_share_rate_key_no_ad):
  646. redis_dt = datetime.datetime.strftime(now_date - datetime.timedelta(days=1), '%Y%m%d')
  647. group_share_rate_key_no_ad = \
  648. f"{config_.KEY_NAME_PREFIX_GROUP_NO_AD}{share_user_data_key}:{share_user_rule_key}:{redis_dt}"
  649. group_share_rate_no_ad = redis_helper.get_score_with_value(key_name=group_share_rate_key_no_ad, value=mid_group)
  650. # 获取视频不出广告后分享的概率
  651. video_share_rate_key_no_ad = f"{config_.KEY_NAME_PREFIX_VIDEO_NO_AD}{share_video_data_key}:{now_dt}"
  652. if not redis_helper.key_exists(video_share_rate_key_no_ad):
  653. redis_dt = datetime.datetime.strftime(now_date - datetime.timedelta(days=1), '%Y%m%d')
  654. video_share_rate_key_no_ad = f"{config_.KEY_NAME_PREFIX_VIDEO_NO_AD}{share_video_data_key}:{redis_dt}"
  655. video_share_rate_no_ad = redis_helper.get_score_with_value(key_name=video_share_rate_key_no_ad,
  656. value=int(video_id))
  657. if video_share_rate_no_ad is None:
  658. video_share_rate_no_ad = redis_helper.get_score_with_value(key_name=video_share_rate_key_no_ad, value=-1)
  659. if group_share_rate_with_ad is None or video_share_rate_with_ad is None \
  660. or group_share_rate_no_ad is None or video_share_rate_no_ad is None:
  661. return None
  662. # 计算此次请求出广告后分享的概率
  663. share_rate_with_ad = float(group_share_rate_with_ad) * float(video_share_rate_with_ad)
  664. # 计算此次请求不出广告分享的概率
  665. share_rate_no_ad = float(group_share_rate_no_ad) * float(video_share_rate_no_ad)
  666. # 计算此次请求出广告的收入增益
  667. roi_ad = ecpm / 1000 - float(roi_param) * float(arpu) * (share_rate_no_ad - share_rate_with_ad)
  668. # 收入增益判断
  669. if roi_ad > 0:
  670. # 大于0,出广告
  671. ad_predict = 2
  672. else:
  673. # 否则,不出广告
  674. ad_predict = 1
  675. result = {
  676. 'arpu': arpu,
  677. 'roi_param': roi_param,
  678. 'ad_id': ad_id,
  679. 'ad_type': ad_type,
  680. 'mid_group': mid_group,
  681. 'group_share_rate_with_ad': group_share_rate_with_ad,
  682. 'video_share_rate_with_ad': video_share_rate_with_ad,
  683. 'group_share_rate_no_ad': group_share_rate_no_ad,
  684. 'video_share_rate_no_ad': video_share_rate_no_ad,
  685. 'share_rate_with_ad': share_rate_with_ad,
  686. 'share_rate_no_ad': share_rate_no_ad,
  687. 'roi_ad': roi_ad,
  688. 'ad_predict': ad_predict
  689. }
  690. return result
  691. except Exception as e:
  692. log_.error(traceback.format_exc())
  693. return None