Files
CompanyProject/芳林公司项目/pytest/utils/probability/model.py
2024-09-27 01:32:49 +08:00

240 lines
9.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import math
import numpy as np
from sklearn.svm import SVR
from sklearn.linear_model import Ridge
import requests
class Predictor1:
def __init__(self,
borderline=50, # 当考生分数与录取线相差borderline时即录取率大于99%
sigma=0.0, # 当考生分数等于录取线时概率为50%增加simga则录取概率增加反之减小
mode='svr', # 'diff':差分法, 'svr':机器学习
remove_edges=False, # 是否除去最大最小值
sample_range=4, # 使用最近几年的数据
keep_latest_data=True, # 是否始终使用上一年的数据
# 以下是svr模型的参数
kernel='rbf',
degree=3,
gamma='scale',
coef0=0.0,
tol=1e-3,
C=10.0,
epsilon=0.1,
shrinking=True,
cache_size=100,
verbose=False,
max_iter=-1):
'''
:param borderline: the probability of a score which is lager than borderline will be lager than 99%
:param sigma: control the middle line of probability. when the score equals to the passing score, the
probability will be 50%, if increasing sigma, the probability will larger than 50% otherwise
smaller than 50%
:param mode: 'diff' or 'svr
:param remove_edges: whether remove the max value and min value
:param sample_range: the number of data which is used to train
:param keep_latest_data: whether keep the data of the latest year
:param C: Regularization parameter
:param cache_size: Specify the size of the kernel cache (in MB)
'''
assert sample_range > 0 and borderline > 0
self.scale = 5. / borderline
self.sigma = sigma
self.mode = mode
self.remove_edges = remove_edges
self.sample_range = sample_range
self.keep_latest_data = keep_latest_data
self.kernel = kernel
self.degree = degree
self.gamma = gamma
self.coef0 = coef0
self.tol = tol
self.C = C
self.epsilon = epsilon
self.shrinking = shrinking
self.cache_size = cache_size
self.verbose = verbose
self.max_iter = max_iter
self.model_dict = {}
self.diff_dict = {}
def create_model(self):
return SVR(kernel=self.kernel,
degree=self.degree,
gamma=self.gamma,
coef0=self.coef0,
tol=self.tol,
C=self.C,
epsilon=self.epsilon,
shrinking=self.shrinking,
cache_size=self.cache_size,
verbose=self.verbose,
max_iter=self.max_iter)
def reset_models(self):
self.model_dict = {}
def modified_sigmoid(self, x):
return 1. / (1. + math.exp(-((x + self.sigma) * self.scale)))
def train(self, infos, data):
'''
:param infos: tuple (school, province, wenli, batch)
:param data: list[list] [[year, pro_cro, min_score]]
'''
assert isinstance(infos, tuple) and isinstance(data, list) and len(data) > 0
data.sort(key=lambda x: x[0], reverse=True)
if self.mode == 'svr':
if infos not in self.model_dict:
model = self.create_model()
self.model_dict[infos] = model
else:
model = self.model_dict[infos]
data = data[:self.sample_range]
train_x, train_y = [[d[0], d[1]] for d in data], [d[2] for d in data]
model.fit(train_x, train_y)
elif self.mode == 'diff':
N = self.sample_range + 2 if self.remove_edges else self.sample_range
diffs = [data[i][2] - data[i][1] for i in range(min(N, len(data)))]
if len(data) < N or not self.remove_edges:
self.diff_dict[infos] = sum(diffs) / len(diffs)
else:
if self.keep_latest_data:
self.diff_dict[infos] = (sum(diffs) - max(diffs[1:]) - min(diffs[1:])) / (len(diffs) - 2)
else:
self.diff_dict[infos] = (sum(diffs) - max(diffs) - min(diffs)) / (len(diffs) - 2)
else:
raise ValueError('invalid mode')
def predict(self, infos, data):
'''
:param infos: tuple [school, province, wenli, batch]
:param data: list[list] [[year, pro_cro, score]]
'''
assert isinstance(infos, tuple) and isinstance(data, list)
if self.mode == 'svr':
if infos not in self.model_dict:
raise ValueError("the model hasn't been trained")
else:
model = self.model_dict[infos]
pred_x, scores = [[d[0], d[1]] for d in data], [d[2] for d in data]
lines = model.predict(pred_x)
probabilities = [self.modified_sigmoid(scores[i] - lines[i]) for i in range(len(lines))]
elif self.mode == 'diff':
if infos not in self.diff_dict:
raise ValueError("the difference hasn't been calculated")
else:
diff = self.diff_dict[infos]
probabilities = [self.modified_sigmoid(data[i][2] - (data[i][1] + diff)) for i in range(len(data))]
else:
raise ValueError('invalid mode')
return probabilities
class Predictor2:
def __init__(self, borderline=20, previour_years_num=3, alpha=100, sigma=3.1 + 0.09):
self.borderline = borderline # 当考生分数与录取线相差borderline时即录取率大于99%
self.previour_years_num = previour_years_num # 使用过去几年的数据
self.alpha = alpha
self.scale = sigma / borderline
self.norm_arr = np.load('/Applications/DeskTop/公司项目/芳林公司项目/pytest/utils/probability/norm_arr.npy')
def __predict_lowest_rank(self, previous_data, year):
'''
:param previous_data: list[list] [[year, low_sort]]
:param year: int
:return:
'''
model = Ridge()
train_x, train_y = [[(x[0] % 100) * self.alpha] for x in previous_data], [x[1] for x in previous_data]
model.fit(train_x, train_y)
return model.predict([[(year % 100) * self.alpha]])[0]
def __query_score(self, province, wenli, weici, base_url='http://www.zhiyuanhelp.com/index.php/api/getweici?'):
url = base_url + 'province=%d&wenli=%d&weici=%d' % (province, wenli, weici)
try:
score = int(requests.get(url).text)
except:
raise ValueError('query_score: fail to query the score')
return score
def __calculate_probability(self, difference):
table = {-1: 37, 0: 45, 1: 55, 2: 63, 3: 73, 4: 80, 5: 96, 12: 96, 19: 99}
r=np.random.randn()
if difference < 0:
difference = -difference
positive = False
else:
positive = True
if difference > self.borderline:
return 1 if positive else 0
else:
difference *= self.scale
h, w = np.shape(self.norm_arr)
i, j = 1, 1
while i < h:
if (i == h - 1) or (self.norm_arr[i][0] <= difference and difference < self.norm_arr[i + 1][0]):
break
i += 1
difference -= self.norm_arr[i][0]
while j < w:
if (j == w - 1) or (self.norm_arr[0][j] <= difference and difference < self.norm_arr[0][j + 1]):
break
j += 1
return self.norm_arr[i][j] if positive else 1 - self.norm_arr[i][j]
def predict(self, province, wenli, previous_data, previous_lowest_score, current_year, current_ranks):
'''
:param province: int 省份代码
:param wenli: int 文理代码
:param previous_data: list[list] [[year, low_sort]] 要预测的学校前几年的年份和最低录取位次
:param previous_lowest_score: int 要预测的学校上一年的最低录取分数
:param current_year: int 要预测的年份(今年)
:param current_ranks: list[int] 要预测的学生位次(今年),可输入多个
'''
previous_data = previous_data[:self.previour_years_num]
previous_data.sort(key=lambda x: x[0], reverse=True)
current_lowest_rank = self.__predict_lowest_rank(previous_data, current_year)
previous_lowest_rank = previous_data[0][1]
previous_ranks = [current_rank - current_lowest_rank + previous_lowest_rank for current_rank in current_ranks]
previous_scores = [self.__query_score(province, wenli, previous_rank) for previous_rank in previous_ranks]
probabilities = [self.__calculate_probability(previous_score - previous_lowest_score) for previous_score in
previous_scores]
return probabilities
# model = Predictor2()
# print(model.predict(844, 2, [[2016, 11231], [2017, 12310], [2018, 12111]], 561, 2019, [14056]))