User Allocation In MEC: A DRL Approach 论文笔记

news2024/11/15 17:38:58

论文:ICWS 2021 移动边缘计算中的用户分配:一种深度强化学习方法

代码地址:使用强化学习在移动边缘计算环境中进行用户分配

目录

Ⅰ.Introduction

II. MOTIVATION-A.验证假设的观察结果 

II. MOTIVATION-A Motivating Example

数据驱动方法的基本思想

III.强化学习分配

RL框架

 RL Allocation Algorithm代码

IV.确定性方法作为BASELINE

 ILP Algorithm整数线性规划(ILP)算法代码

Nearest Neighbourhood Greedy Algorithm代码 

整体框架代码:

框架中用到的函数:

对比实验:

V.实验与结果分析


Ⅰ.Introduction

随着对低延迟需求的增加,边缘计算或雾计算逐渐成为主流。当前最先进的技术假设边缘服务器上的总资源利用率等于从边缘服务器提供的所有服务的资源利用率总和。然而,边缘服务器资源利用率服务请求数量之间通常存在高度非线性的关系,尤其CPU-GPU协同执行,使资源利用率的数学建模异常复杂。

Motivation目前用于解决边缘用户分配(EUA问题的算法普遍假设服务的资源利用率边缘服务器上提供的服务数线性关系,假设服务器的总资源利用率每个服务请求的资源占用量累积总和。然而,实际服务过程中资源使用量是高度动态的,难以通过数学建模精确描述。

Method提出一种设备端深度强化学习(DRL框架来解决边缘用户分配(EUA问题,基于与 MEC 系统的经验和交互逐步学习适当的资源分配DRL Agent在服务延迟阈值约束下学习在某边缘服务器上服务的用户数量DRL Agent通过同时观察系统参数,直接从边缘服务器中学习非线性依赖关系。

Conclusion实验评估表明,DRL框架在用户分配数量方面优于传统的确定性方法。

II. MOTIVATION-A.验证假设的观察结果 

分别使用 GPU 和不使用 GPU 观察目标检测应用程序 YOLO处理图像的服务执行时间:

非线性关系:YOLO执行时间CPUGPU参数之间的关系是非线性的。表明执行时间不仅仅取决于单一的参数变化,还受到许多隐含因素的影响,例如CPU/GPU的可用性。

复杂性:由于存在多个隐藏参数,精确建模YOLO执行时间与系统资源之间的关系是相对困难的。

建模服务执行时间困难:

•可用处理器资源和执行时间之间的非线性关系:可用处理器资源执行时间之间存在非线性关系,内核数量少时减少内核数量对执行时间的影响更显著,在高工作负载情况下增加后台工作负载会显著减慢执行速度。

• 跨时间的变化:相同配置的同一台机器上执行时间也存在显着差异,受服务调用模式和温度等多个隐藏参数影响。

• 跨服务的变化不同服务的执行时间模式各异,如 Yolo 执行时间随用户增加近似线性增长,而 MobileNet 表现为非线性,使建模任务复杂化。

II. MOTIVATION-A Motivating Example

七个移动用户 U1、U2 .. U7和两个边缘服务器 e1 和 e2,每个用户请求边缘服务器上可用的两个服务 s1 s2 之一,用户U1U2U4U6请求服务s1,其余用户请求服务s2,每个边缘服务器由一个资源向量4 元组(Available RAMCore 的数量、CPU 后台工作负载%GPU Utilized%)。

解释传统的确定性方法如何导致用户到服务器的分配效率低下:

如对于边缘服务器 e1,服务s1的单个用户请求的执行时间为 3.12s,四个用户的预期执行时间用线性插值得到12.48 s。然而实际 3.468 s

假设延迟阈值6.58 秒,用仅考虑服务单个请求的执行时间的确定性方法U1U2U3分配给e1,只会给s1分配2个用户(每个用户3.12s),给s2分配1个用户(6.32s

使用确定性方法分配的用户总数为 3

数据驱动方法的基本思想

数据驱动方法基于实际的执行时间数据更精确的资源利用模型进行用户到服务器的分配,克服确定性方法的缺点,提供更有效的资源分配。

运行 YOLO 的四个用户的执行时间低于 6.55 的延迟阈值。实际上可以在 e1 上分配用户 U1U2U4(只需 3.35)e2 可以容纳 U5 U7(只需 6.12)。

使用数据驱动的方法分配总共 5 个用户(比确定性方法多 2 个用户),更准确地建模资源利用率。

III.强化学习分配

MEC环境E=\{e_1,e_2...e_j\}中每个边缘服务器e_j的覆盖率半径为r_j。边缘服务器覆盖半径内的移动users U=\{U_1,U_2...U_i\}可以请求托管在该服务器上的服务S=\{s_1,s_2...s_k\} 。每个边缘服务器上可用资源(RAMCoresCPU 背景工作负载%GPU Utilized%

分配策略根据动态因素确定用户与特定边缘服务器的绑定

a)新用户加入边缘服务器的覆盖区域  (b)用户远离边缘服务器的覆盖区域   (c)用户服务请求更改  (d)边缘服务器或移动用户离线

分配策略目标是在遵守服务执行的延迟阈值𝛤Γ的同时尽可能多满足服务请求传统的确定性方法依赖历史数据预测执行时间,但由于执行时间的动态特性,可能导致资源分配过度或不足。提出的RL学习框架通过从边缘服务器的实际经验中学习服务执行模式,实时优化用户-服务器绑定决策,更有效地应对动态环境。

RL框架

RL 框架中的Agent通过探索环境并从动作接收反馈来学习环境以选择更好的动作选择,RL可以在不需要大量标记数据的情况下学习底层环境。

在这个 RL 框架中,Agent不断地与边缘服务器交互以采取行动,执行多个服务请求并根据执行占用空间获得相应的奖励。

状态:边缘服务器的资源向量以及服务请求的数量

动作:等待在边缘服务器上执行的服务请求集。

Reward的计算: 

#compute latency
    def get_reward(self, state, action):
        #将动作 action 转换为两个用户数量 u1 和 u2
        
        u1 = action//5 + 1
        u2 = (action+1) - (u1-1)*5
        #sample time from dataframe
        gram = state[0]
        gcores = state[1]
        gwl_c = state[2]
        gwl_g = state[3]
        gs1 = u1*100
        gs2 = u2*100
        #查找与当前状态和动作匹配的记录
        fetch_state = self.df.loc[ (self.df['ram'] == gram) & (self.df['cores']== gcores) & (self.df['workload_cpu']==gwl_c) & (self.df['workload_gpu']==gwl_g) & (self.df['users_yolo']==gs1) & (self.df['users_mnet']==gs2)]
        #计算奖励:        
        if fetch_state.empty:#找不到匹配的状态信息,则返回较大的负奖励,表示这是一个不利的动作选择
            return -20 
        # 计算网络延迟:
        time1 = fetch_state.sample().iloc[0]['time_yolo'] #从匹配的状态信息中随机选择一个样本
        time2 = fetch_state.sample().iloc[0]['time_mnet']#获取 time_yolo 和 time_mnet 的延迟时间
        tm = max(time1, time2)#两者中较大的延迟时间用作网络延迟的阈值
        #add total latencies due to network based on number of u1 and u2
        
        if (tm <= latency_threshold): #用户数量的变化对应的奖励,以及动作本身的基础奖励(u1 + u2)
            return  0.01*(gs1 - state[4]) +  0.01*(gs2 - state[5]) + u1 + u2 

        else:
            return -5 - u1 - u2  

 训练RL Agent

RL环境:

import numpy as np
import gym
from gym import spaces
from gym.utils import seeding


class yolosystem(gym.Env):
    metadata = {'render.modes': ['human']}
    
    def __init__(self, n_actions, filename):
        
        super(yolosystem, self).__init__()
        
        self.n_actions = n_actions #total number of action space after ranging [10, 20, 30 ...]
        self.action_space = spaces.Discrete(self.n_actions) #total number of users in the action space; starts with zero
        self.observation_space = spaces.Box(low=np.array([0,0,0,0,0,0]), high=np.array([11000]*6), shape=(6, ), dtype=np.int32) #<RAM, Core, Workload>
        self.seed()
        self.current_obs = np.array( [3000, 2, 40, 2, 100, 100] ) #current observation = <ram, cores, workload%>

        #Load dataset
        self.df = pd.read_csv(filename)
        # computer percentage of GPU usage from actual use
        self.df['workload_gpu'] = self.df['workload_gpu'].multiply(1/80).round(0).astype(int) #round gpu workload

        #get unique data in set
        self.ram = self.df.ram.unique()
        self.cores = self.df.cores.unique()
        self.workload_cpu = self.df.workload_cpu.unique()
        print(self.df) #print dataset
       
        

    def seed(self, seed=1010):
        self.np_random, seed = seeding.np_random(seed)
        return [seed]

    def step(self, action):
        assert self.action_space.contains(action) #action should be in action space
        state = self.current_obs
        done = True #Episodes ends after each action

        #compute latecy from the number of users
        reward = self.get_reward(state, action) #linear latency           
#         print(action, reward)
        self.current_obs = self.get_random_state() #go to a random state
        
#         print(self.current_obs)
        return self.current_obs, reward, done, {} #no-states, reward, episode-done, no-info

    def reset(self):
        self.current_obs = self.get_random_state()
        return self.current_obs #current state of the system with no load

    def render(self, mode='human', close=False):
        print(f"Current State:<{self.current_obs}>")
        
    
    #compute latency
    def get_reward(self, state, action):
        #将动作 action 转换为两个用户数量 u1 和 u2
        
        u1 = action//5 + 1
        u2 = (action+1) - (u1-1)*5
        #sample time from dataframe
        gram = state[0]
        gcores = state[1]
        gwl_c = state[2]
        gwl_g = state[3]
        gs1 = u1*100
        gs2 = u2*100
        #查找与当前状态和动作匹配的记录
        fetch_state = self.df.loc[ (self.df['ram'] == gram) & (self.df['cores']== gcores) & (self.df['workload_cpu']==gwl_c) & (self.df['workload_gpu']==gwl_g) & (self.df['users_yolo']==gs1) & (self.df['users_mnet']==gs2)]
        #计算奖励:        
        if fetch_state.empty:#找不到匹配的状态信息,则返回较大的负奖励,表示这是一个不利的动作选择
            return -20 
        # 计算网络延迟:
        time1 = fetch_state.sample().iloc[0]['time_yolo'] #从匹配的状态信息中随机选择一个样本
        time2 = fetch_state.sample().iloc[0]['time_mnet']#获取 time_yolo 和 time_mnet 的延迟时间
        tm = max(time1, time2)#两者中较大的延迟时间用作网络延迟的阈值
        #add total latencies due to network based on number of u1 and u2
        
        if (tm <= latency_threshold): #用户数量的变化对应的奖励,以及动作本身的基础奖励(u1 + u2)
            return  0.01*(gs1 - state[4]) +  0.01*(gs2 - state[5]) + u1 + u2 

        else:
            return -5 - u1 - u2     
        
    
    #get to some random state after taking an action
    def get_random_state(self):
        #generate state randomly
        gram = np.random.choice(self.ram, 1)[0]
        gcores = np.random.choice(self.cores, 1)[0]
        gwl_c = np.random.choice(self.workload_cpu, 1)[0]
        
        #fetch gamma for the state
        fetch_state = self.df.loc[ (self.df['ram'] == gram) & (self.df['cores']== gcores) & (self.df['workload_cpu']==gwl_c) ]
        gwl_g = fetch_state.sample().iloc[0]['workload_gpu'] #fetch workload randmoly
        
        gs1 = random.randrange(50, 550, 50)
        gs2 = random.randrange(50, 550, 50)
        
        return np.array( [gram, gcores, gwl_c, gwl_g, gs1, gs2] )
from stable_baselines3.common.monitor import Monitor
import os
# Create log dir
log_dir = './agent_tensorboard/'
os.makedirs(log_dir, exist_ok=True)

env = Monitor(env, log_dir)

from stable_baselines3 import DQN
from stable_baselines3.dqn import MlpPolicy
from stable_baselines3.common.vec_env import DummyVecEnv

# wrap it非向量化的环境 env 转换为一个向量化的环境 env
env = DummyVecEnv([lambda: env])

 

训练 

model = DQN(MlpPolicy, env, verbose=0, tensorboard_log = log_dir, exploration_fraction=0.4, learning_starts=150000,  train_freq=30, target_update_interval=30000, exploration_final_eps=0.07)

begin = time.time()
model.learn(total_timesteps=500000) #reset_num_timesteps=False
end = time.time()
training_time = end-begin

 RL Allocation Algorithm代码

#Load model
def rl_algo():
    #对于每台服务器,使用RL预测每个服务器的容量
    server_capacity = np.zeros((N, S))
    for server_id in range(N):
        state = server_state[server_id]
#         if model_type == 'lin':
        action = model_rl.predict(np.array(state), deterministic=True)
#         if model_type == 'exp':
#             action = model_exp.predict(np.array(state), deterministic=True)
# 根据action计算两种服务的预测容量 (u1 和 u2)
        u1 = action[0]//5 + 1
        u2 = (action[0]+1) - (u1-1)*5
        server_capacity[server_id][0] = u1*100 #model output
        server_capacity[server_id][1] = u2*100 #model output
    
    #根据每个用户的服务器数进行排序
    col1 = np.array([np.sum(ngb,axis=1)])
    col2 = np.array([np.arange(U)])
    sorted_ngb = np.concatenate((ngb, col1.T, col2.T), axis=1) #add rowsum and index column添加行和索引列
    sorted_ngb = sorted_ngb[np.argsort(sorted_ngb[:, N])] #sort the rows based on rowsum column根据行和列对行进行排序

    #分配用户到服务器
    #run allocation algorithm
    rl_allocation = []
# 遍历用户,根据用户连接的服务器列表和服务请求,选择最大预测容量的服务器分配。服务器有足够容量则更新服务器容量并记录分配结果
    for i in range(U):
        server_list = np.where(sorted_ngb[i, :N] == 1)[0] #获取用户连接到的服务器列表

        if len(server_list) == 0: #跳过没有服务器的用户
            continue
        
        ser = int(service[i]) #用户正在请求哪个服务
        choosen_server = server_list[np.argmax(server_capacity[server_list, ser])] #找到所选服务器的 ID
        
        
        if  server_capacity[choosen_server][ser] > 0: #将用户分配给choosen_server
            server_capacity[choosen_server][ser] -= 1 #减少服务器容量

            rl_allocation.append( (int(sorted_ngb[i, N+1]), choosen_server) ) #(user, server) alloc pair
    print('RL Num of allocation: {}'.format(len(rl_allocation)))

    return rl_allocation

IV.确定性方法作为BASELINE

使用历史服务执行数据的平均值确定边缘服务器e_j上服务S_k的执行时间\gamma _{kj},进而确定可以分配到边缘服务器的用户数量的相应代码:allocation.ipynb  def generate_server_state(num_server)

#获取与选择的 GPU 工作负载值匹配的行 计算YOLO 和 MNet 的平均时间
        time_yolo = fetch_time['time_yolo'].mean() #average of time for particular state
        time_mnet = fetch_time['time_mnet'].mean()
        # 根据每个服务器的服务请求分配状态
        gs1 = server_service[s_id][0]
        gs2 = server_service[s_id][1] 
        server_state.append( [gram, gcores, gwl_c, gwl_g, gs1, gs2] )
        # 追加每个服务器的 gamma 值
        gamma.append((time_yolo, time_mnet)) #append the gamma value of each server

 ILP Algorithm整数线性规划(ILP)算法代码

解决用户分配到服务器的问题,目标是最大化覆盖的用户数量

def ilp_algo():
    ## ===================================ILP with python mip
    # >> solver_name=GRB
    # >> Currently using CBC
    I = range(U) #user 用户的范围
    J = range(N) #server服务器的范围
    alloc = Model(sense=MAXIMIZE, name="alloc", solver_name=CBC)
    alloc.verbose = 0
    def coverage(user_ix, server_ix):
        if ngb[user_ix][server_ix]==1:
            return 1
        else:
            return 0

    #U: num of users, N: num of servers
    # 创建二进制变量矩阵 x,其中 x[i][j] 表示用户 i 是否被分配到服务器 j
    x = [[ alloc.add_var(f"x{i}{j}", var_type=BINARY) for j in J] for i in I]

    #Objective Equation
    # 目标函数:最大化分配的用户数量
    alloc.objective = xsum( x[i][j]  for i in I for j in J )

    #1. 覆盖约束
    for i in I:
        for j in J:        
                if not coverage(i,j):
                    alloc += x[i][j] == 0

    # 2. 每个用户只能被分配到一个服务器
    for i in I:
        alloc += xsum( x[i][j] for j in J ) <=1

    # 3. 延迟约束
    for j in J:
        alloc += xsum( gamma[j][int(service[i])]*x[i][j] for i in I ) <=latency_threshold-network_latency[j] 

    #alloc.write("test-model.lp")

    #===========Start Optimization=========
    alloc.optimize(max_seconds=25)# 优化模型

    #==========ILP Ends here
    #print(f"Number of Solutions:{qoe.num_solutions}")
    ilp_allocation = [ (i,j) for i in I for j in J if x[i][j].x >= 0.99] # 获取分配结果

    #print(f"Number of Solutions:{qoe.num_solutions}")
    #print(f"Objective Value:{qoe.objective_value}")
    allocated_num_users = len(ilp_allocation)
    print("ILP Allocated Num of Users: {}".format(allocated_num_users))# 输出分配的用户数量
    # selected.sort()
    return ilp_allocation

Nearest Neighbourhood Greedy Algorithm代码 

def greedy_algo():
    server_capacity = np.zeros(N)# 初始化服务器容量数组
    rl_allocation = []
    for user_id in range(U):#获取与用户连接的服务器列表
        server_ngb_list = np.where(ngb[user_id, :N] == 1)[0] #get the list of server to which user is connected
        if len(server_ngb_list) == 0: #ignore the users which are not under any servers
            continue 
        # 计算每个用户到各个服务器的距离并排序
        #find the distance to each users in the server_ngb_list
        dist_list = np.array([ server_ngb_list, [server.iloc[i]['geometry'].centroid.distance(user.iloc[user_id]['geometry']) for i in server_ngb_list] ])
        # sorted list of servers based on the distance from users
        sorted_distance_list = dist_list[ :, dist_list[1].argsort()]
        #get the list of servers arranged in least to max distance
        server_list = sorted_distance_list[0].astype(int)
        # 分配算法
        lat = 0
        for server_id in server_list:
            lat = gamma[server_id][int(service[user_id])]#根据用户请求的服务类型和服务器,获取相应的服务延迟
            if server_capacity[server_id]+lat <= latency_threshold-network_latency[server_id]:
                server_capacity[server_id] += lat #increment the server_capacity of server
                rl_allocation.append( (user_id, server_id) ) #(user, server) alloc pair
                break
    
    print('Greedy-Ngb Num of allocation: {}'.format(len(rl_allocation)))
    return rl_allocation

整体框架代码:

固定服务器数量更改用户数量的情况:

对于不同用户数量,先拿到用户和服务器之间的邻居矩阵ngb,并计算网络延迟network_latency

之后生成服务器状态server_state 计算每个服务器的gamma 值 generate_server_state(num_server)

之后分别使用三种算法计算成功分配的数量

if alloc_type == 'server': #服务器固定,变化用户数量"
    for U in range(100, 600, 100):#用户数量100-500
        for epoch in range(50):
            print("User:", U, 'Server:', N, 'Epoch:', epoch)
            ngb, user, server, service, server_service, network_latency = ngb_matrix(U, N, S) #从EUA数据生成服务器和用户 # 确定邻域矩阵
            server_state, gamma = generate_server_state(N) #为每个用户分配状态和γ值

            #=======ILP starts
            start = 0
            stop = 0
            execution_time_ilp = 0
            start = timeit.default_timer()

            ilp_aloc = ilp_algo() #call ILP algorithm

            stop = timeit.default_timer()
            execution_time_ilp = stop - start
            #========ILP ends

            #=======Greedy starts
            start = 0
            stop = 0
            execution_time_greedy = 0
            start = timeit.default_timer()

            greedy_aloc = greedy_algo() #call ILP algorithm

            stop = timeit.default_timer()
            execution_time_greedy = stop - start
            #========Greedy ends

            #=======RL_linear starts
            start = 0
            stop = 0
            execution_time_rl = 0
            start = timeit.default_timer()

            rl_aloc = rl_algo() #call ILP algorithm

            stop = timeit.default_timer()
            execution_time_rl = stop - start
            #========RL_linear ends


            #========Store results to file
            to_append = [U, N,
                         len(ilp_aloc), execution_time_ilp,
                         len(greedy_aloc), execution_time_greedy,
                         len(rl_aloc), execution_time_rl,
                        ] 
            dseries = pd.Series(to_append, index = result_user.columns)
            result_user = result_user.append(dseries, ignore_index=True)
            print("epoch:", epoch)
    result_user.to_csv(result_file, index=False)

框架中用到的函数:

一、生成服务器状态计算每个服务器的 gamma 值

def generate_server_state(num_server):#生成服务器状态计算每个服务器的 gamma 值
    df = pd.read_csv(filename_base)
 # 将 GPU 工作负载的数值进行缩放和四舍五入   
#     df['ram'] = df['ram'].div(1000).round(0).astype(int)
#     df['workload_cpu'] = df['workload_cpu'].div(10).round(0).astype(int)
    df['workload_gpu'] = df['workload_gpu'].multiply(1/80).round(0).astype(int) #round gpu workload
#     df['users_yolo'] = df['users_yolo'].div(100).round(0).astype(int)
#     df['users_mnet'] = df['users_mnet'].div(100).round(0).astype(int)
    
    
    #get unique data in set获取数据集中唯一的 RAM、核心数和 CPU 工作负载值
    ram = df.ram.unique()
    cores = df.cores.unique()
    workload_cpu = df.workload_cpu.unique()

    
    server_state = []#服务器状态
    gamma = []
    
    for s_id in range(num_server):
        #对于每一个服务器,随机选择一个 RAM、核心数和 CPU 工作负载值
        gram = np.random.choice(ram, 1)[0]
        gcores = np.random.choice(cores, 1)[0]
        gwl_c = np.random.choice(workload_cpu, 1)[0]
       
        #fetch gamma for the state获取对应状态的行
        fetch_state = df.loc[ (df['ram'] == gram) & (df['cores']== gcores) & (df['workload_cpu']==gwl_c) ]
    #    从匹配的状态中随机选择一个 GPU 工作负载值
        gwl_g = fetch_state.sample().iloc[0]['workload_gpu'] #fetch workload randmoly
        fetch_time = fetch_state.loc[ (df['workload_gpu'] == gwl_g) ]
        #获取与选择的 GPU 工作负载值匹配的行 计算YOLO 和 MNet 的平均时间
        time_yolo = fetch_time['time_yolo'].mean() #average of time for particular state
        time_mnet = fetch_time['time_mnet'].mean()
        # 根据每个服务器的服务请求分配状态
        gs1 = server_service[s_id][0]
        gs2 = server_service[s_id][1] 
        server_state.append( [gram, gcores, gwl_c, gwl_g, gs1, gs2] )
        # 追加每个服务器的 gamma 值
        gamma.append((time_yolo, time_mnet)) #append the gamma value of each server
    
    return server_state, gamma

二、计算邻接矩阵

#================neighbourhood Computing
def ngb_matrix(U, N, S):
# 生成用户和服务器之间的邻居矩阵,并计算网络延迟 
    #U: number of users
    #N: number of servers
    #S: number of services
    # U X N matrix
    user = load_users(U)
    server = load_servers(N)
    neighbourhood = np.zeros([U, N]) #用户和服务器之间的邻居矩阵
    network_latency = np.zeros(N) #每个服务器的网络延迟
    latency_data = load_planetlab() #加载 PlanetLab 数据,返回一个距离矩阵(bin size 150)
    # 检查每个用户是否在服务器的缓冲区内,并计算网络延迟
    for u in range(0, U):
        for n in range(0, N):
            #检查用户是否在服务器的缓冲区内(使用几何空间的 contains 方法)
            if server.iloc[n].geometry.contains(user.iloc[u].geometry):
                neighbourhood[u,n]=1#邻居矩阵中相应位置设为 1
                # 计算距离并分配延迟
                distance = server.iloc[n].geometry.centroid.distance(user.iloc[u].geometry)
                rep_lat = fetch_network_lat(int(distance), latency_data) #根据距离从 latency_data 中获取网络延迟
                if network_latency[n] < rep_lat:#最大可能延迟
                    network_latency[n] = rep_lat
                
            else:
                neighbourhood[u,n]=0
    
    service = np.zeros(U)
    
    #为用户分配服务请求
    for u in range(0, U):#为每个用户随机分配一个从 0 到 S-1 的服务请求
        service[u] = random.randrange(0, S, 1) 
        
    server_service = np.zeros((N, S))
    
    #统计每个服务器的服务请求数量
    for n in range(0, N):
        for u in range(0, U):
            if neighbourhood[u][n] == 1:
                server_service[n][int(service[u])] += 1
                
                
    
    return neighbourhood, user, server, service, server_service, network_latency

三、计算邻接矩阵中用到的函数

#================Load Planet Lab data
# 加载 PlanetLab 数据并转换为一个矩阵格式
def load_planetlab():
    #Convert to triangle
    ldata = np.loadtxt('eua/PlanetLabData_1')[np.tril_indices(490)]
    ldata = ldata[ ldata != 0]#提取下三角矩阵的非零值
    ldata =np.unique(ldata)#去重并重置数据大小,使其符合150行的矩阵格式
    np.set_printoptions(suppress=True)
    length = ldata.shape[0]
    latency_row = 150
    latency_col = (length//latency_row) #Global Data used
    ldata = np.resize(ldata, latency_col*latency_row)
    latency = ldata.reshape(latency_row,-1)
    return latency

#=================Fetch Network latency
# 根据距离从延迟数据中获取网络延迟
def fetch_network_lat(distance, latency_data):
    rep_lat = np.random.choice(latency_data[distance], size=1, replace=True)#根据距离从延迟数据中随机选择一个延迟值
    return rep_lat/1000 #将延迟值转换为秒
    

#===============User Data
# 加载用户数据并转换为地理数据格式
def load_users(num_of_users):
    user_raw = pd.read_csv("eua/users.csv")
    user_raw = user_raw.rename_axis("UID")#将数据框的索引轴重命名为 "UID",即用户的唯一标识符
    df = user_raw.sample(num_of_users)#随机抽样指定数量的用户数据
# 创建地理数据框,使用Longitude和Latitude列创建点几何对象,并转换坐标参考系统(CRS)
    gdf = geopandas.GeoDataFrame(df, geometry = geopandas.points_from_xy(df.Longitude, df.Latitude), crs = 'epsg:4326')#创建地理数据框
    user = gdf [['geometry']] #保留geometry列
    user = user.to_crs(epsg=28355) #指定数据的坐标参考系统(WGS84投影)
    #Insert additional data to dataframe
    #user = user.apply(add_data, axis=1)
    return user

#================Server Data
def load_servers(num_of_servers):
    # 加载服务器数据,并将其转换为地理数据格式
    server_raw = pd.read_csv("eua/servers.csv")
    server_raw = server_raw.rename_axis("SID")#将数据框的索引轴重命名为 "SID",即服务器的唯一标识符
    df = server_raw.sample(num_of_servers) #随机抽样指定数量的服务器
    gdf = geopandas.GeoDataFrame(df, geometry = geopandas.points_from_xy(df.LONGITUDE, df.LATITUDE), crs = 'epsg:4326')#创建地理数据框
    server = gdf [['geometry']] #Keep Geometry column
    server = server.to_crs(epsg=28355) #Cover to crs in Australian EPSG
    def add_radius(series):
#         radius = random.randrange(150, 250, 10)
# 为每个服务器添加一个固定半径的缓冲区
        radius = 150 #每个服务器的缓冲区半径设为固定值 150
        series.geometry = series.geometry.buffer(radius)
        series['radius'] = radius
#         series['resource'] = tcomp
        return series
    server = server.apply(add_radius, axis = 1)
    return server


#绘制用户和服务器数据在地图上的分布情况
def plot_data(user, server):
    %config InlineBackend.figure_format='retina'
    %matplotlib inline

    cbd = geopandas.read_file('eua/maps', crs = {'init': 'epsg=28355'} ) #read cbd-australia location data

    fig, ax = plt.subplots(1, 1, figsize=(15,10))
    ax.set_aspect('equal')

    ax.set_xlim(319400, 322100)
    ax.set_ylim(5811900, 5813700)

    user.plot(ax=ax, marker='o', color='red', markersize=20, zorder=3, label="users")
    server.plot(ax =ax, linestyle='dashed', edgecolor='green', linewidth=1, facecolor="none", zorder=1)
    server.centroid.plot(ax=ax, marker='s', color='blue', markersize=50, zorder=2, label="server")
    cbd.plot(ax=ax, color='grey', zorder=0, alpha = 0.3);

    ax.set_title("MEC Environment(EUA): CBD Melbourne(Australia)")
    ax.legend(bbox_to_anchor=(1, 0), loc='lower left')
    plt.show()

运行结果:

对比实验:

一、对于RL算法 使用不同训练回合数:

rl_algo_prop()rl_algo_und()只是用了两个不同程度训练的agent模型,其余部分完全一致,这里只展示rl_algo_prop()

分别是训练30,000回合的RL Agent生成的分配数量和训练1,50,000回合

动作空间的量化大小\lambda= 2

model_und = DQN.load("trained_agents/edge_agent_under_train")
model_prop = DQN.load("trained_agents/edge_agent_proper_train")
#Load model
def rl_algo_prop():
    #...同rl_algo()
        #换个模型就OK 
        action = model_prop.predict(np.array(state), deterministic=True)
        print('Actionprop: {}'.format(action))
        u1 = (action[0]//10)*2 + 1
        u2 = (action[0]%10)*2 + 1
        server_capacity[server_id][0] = u1 #model output
        server_capacity[server_id][1] = u2 #model output
    
    #...同rl_algo()

二、对于RL算法 使用不同量化因子:

其余两种只在模型和和动作空间映射改变

rl_algo_act() 代码中说是\lambda=5:

action = model_act.predict(np.array(state), deterministic=True)
        print('Actionact: {}'.format(action))
        
        u1 = (action[0]//5)*4 + 1 #25 action space
        u2 = (action[0]%5)*4 + 1
        server_capacity[server_id][0] = u1 #model output
        server_capacity[server_id][1] = u2 #model output

 rl_algo_thres10()代码中说是\lambda=100:

 action = model_thres10.predict(np.array(state), deterministic=True)
        print('Actionthres10: {}'.format(action))

        u1 = action[0]//5 + 1
        u2 = (action[0]+1) - (u1-1)*5
        server_capacity[server_id][0] = u1*100 #model output
        server_capacity[server_id][1] = u2*100 #model output

对于训练不同回合数rl_algo_prop中动作空间的量化大小\lambda= 2的个人理解,不一定对:

每次agent预测出action之后,从中还原出两个服务s1、s2上的服务请求数(动作)使用的方法不同,\lambda= 2时的映射方法如下,输出action

for action in range(25):#rl_algo_prop()
    u1 = (action//10)*2 + 1
    u2 = (action%10)*2 + 1
    print(f"Action: {action}, u1: {u1}, u2: {u2}")

Action: 0, u1: 1, u2: 1
Action: 1, u1: 1, u2: 3
Action: 2, u1: 1, u2: 5
Action: 3, u1: 1, u2: 7
Action: 4, u1: 1, u2: 9
Action: 5, u1: 1, u2: 11
Action: 6, u1: 1, u2: 13
Action: 7, u1: 1, u2: 15
Action: 8, u1: 1, u2: 17
Action: 9, u1: 1, u2: 19
Action: 10, u1: 3, u2: 1
Action: 11, u1: 3, u2: 3
Action: 12, u1: 3, u2: 5
Action: 13, u1: 3, u2: 7
Action: 14, u1: 3, u2: 9
Action: 15, u1: 3, u2: 11
Action: 16, u1: 3, u2: 13
Action: 17, u1: 3, u2: 15
Action: 18, u1: 3, u2: 17
Action: 19, u1: 3, u2: 19
Action: 20, u1: 5, u2: 1
Action: 21, u1: 5, u2: 3
Action: 22, u1: 5, u2: 5
Action: 23, u1: 5, u2: 7
Action: 24, u1: 5, u2: 9


文章提到:使用大小为\lambda的量化减少动作空间的基数,\lambda=10时新的动作元组 (2,2) 表示旧动作空间中范围 (11 - 20,11 - 20) 中的所有动作

于是我暂且认为\lambda=2指的是,量化后的动作空间中的一个动作,代表原来动作空间中的两个动作,也就是,第一个动作中的 u1: 1是我们选择来代表原来动作空间中u1: 1、u1: 2;同理u2:1代表u2:1、u2:2。下一个动作就从3开始。

但无法解释后边\lambda=5,\lambda=100情况:

\lambda=5:

for action in range(25):#rl_algo_act()
    u1 = (action//5)*4 + 1
    u2 = (action%5)*4 + 1
    print(f"Action: {action+1}, u1: {u1}, u2: {u2}")

Action: 0, u1: 1, u2: 1
Action: 1, u1: 1, u2: 5
Action: 2, u1: 1, u2: 9
Action: 3, u1: 1, u2: 13
Action: 4, u1: 1, u2: 17
Action: 5, u1: 5, u2: 1
Action: 6, u1: 5, u2: 5
Action: 7, u1: 5, u2: 9
Action: 8, u1: 5, u2: 13
Action: 9, u1: 5, u2: 17
Action: 10, u1: 9, u2: 1
Action: 11, u1: 9, u2: 5
Action: 12, u1: 9, u2: 9
Action: 13, u1: 9, u2: 13
Action: 14, u1: 9, u2: 17
Action: 15, u1: 13, u2: 1
Action: 16, u1: 13, u2: 5
Action: 17, u1: 13, u2: 9
Action: 18, u1: 13, u2: 13
Action: 19, u1: 13, u2: 17
Action: 20, u1: 17, u2: 1
Action: 21, u1: 17, u2: 5
Action: 22, u1: 17, u2: 9
Action: 23, u1: 17, u2: 13
Action: 24, u1: 17, u2: 17
 

\lambda=100:

for action in range(25):#rl_algo_thres10
    u1 = action//5 + 1
    u2 = (action+1) - (u1-1)*5
    print(f"Action: {action+1}, u1: {u1}, u2: {u2}")

 Action: 1, u1: 1, u2: 1
Action: 2, u1: 1, u2: 2
Action: 3, u1: 1, u2: 3
Action: 4, u1: 1, u2: 4
Action: 5, u1: 1, u2: 5
Action: 6, u1: 2, u2: 1
Action: 7, u1: 2, u2: 2
Action: 8, u1: 2, u2: 3
Action: 9, u1: 2, u2: 4
Action: 10, u1: 2, u2: 5
Action: 11, u1: 3, u2: 1
Action: 12, u1: 3, u2: 2
Action: 13, u1: 3, u2: 3
Action: 14, u1: 3, u2: 4
Action: 15, u1: 3, u2: 5
Action: 16, u1: 4, u2: 1
Action: 17, u1: 4, u2: 2
Action: 18, u1: 4, u2: 3
Action: 19, u1: 4, u2: 4
Action: 20, u1: 4, u2: 5
Action: 21, u1: 5, u2: 1
Action: 22, u1: 5, u2: 2
Action: 23, u1: 5, u2: 3
Action: 24, u1: 5, u2: 4
Action: 25, u1: 5, u2: 5

暂且存疑

V.实验与结果分析

B. Experimental Results

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1944939.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

我在百科荣创企业实践——简易函数信号发生器(5)

对于高职教师来说,必不可少的一个任务就是参加企业实践。这个暑假,本人也没闲着,报名参加了上海市电子信息类教师企业实践。7月8日到13日,有幸来到美丽的泉城济南,远离了上海的酷暑,走进了百科荣创科技发展有限公司。在这短短的一周时间里,我结合自己的教学经验和企业的…

C#,.NET常见算法

1.递归算法 1.1.C#递归算法计算阶乘的方法 using System;namespace C_Sharp_Example {public class Program{/// <summary>/// 阶乘&#xff1a;一个正整数的阶乘Factorial是所有小于以及等于该数的正整数的积&#xff0c;0的阶乘是1&#xff0c;n的阶乘是n&#xff0…

【学习笔记】无人机系统(UAS)的连接、识别和跟踪(五)-无人机跟踪

目录 引言 5.3 无人机跟踪 5.3.1 无人机跟踪模型 5.3.2 无人机位置报告流程 5.3.3 无人机存在监测流程 引言 3GPP TS 23.256 技术规范&#xff0c;主要定义了3GPP系统对无人机&#xff08;UAV&#xff09;的连接性、身份识别、跟踪及A2X&#xff08;Aircraft-to-Everyth…

Samtec技术科普小课堂 | 一文入门射频连接器~

【摘要/前言】 在本文中&#xff0c;我们将回到基础知识&#xff0c;了解一下什么是射频连接器。如果您是信号完整性专家&#xff0c;请点击阅读原文访问我们的网站视频&#xff0c;通过我们的网络研讨会视频了解教科书上可能找不到的知识。 如果您是电气工程领域的新手&#…

Prometheus 监控Tomcat等java应用的状态

5月应用服务出现问题&#xff0c;当别的小伙伴问我&#xff0c;有没有Tomcat等应用状态的监控的时候&#xff0c;我有点儿尴尬。所以赶紧抽空部署一下。 在配置之前&#xff0c;就当已经会安装jdk和tomcat了。 一、下载jmx_exporter #linux下 cd /usr/local/prometheus wget …

新增ClamAV病毒扫描功能、支持Java和Go运行环境,1Panel开源面板v1.10.12版本发布

2024年7月19日&#xff0c;现代化、开源的Linux服务器运维管理面板1Panel正式发布了v1.10.12版本。 在这一版本中&#xff0c;1Panel新增了多项实用功能。社区版方面&#xff0c;1Panel新增ClamAV病毒扫描功能、支持Java和Go运行环境&#xff0c;同时1Panel还新增了文件编辑器…

浪潮GS企业管理软件GetChildFormAndEntityList存在反序列化漏洞

一、漏洞简介 浪潮GS 面向大中型集团企业采用SOA 架构和先进开放的GSP 应用中间件开发,形成了集团管控13 大领域15 大行业60余个细分行业的解决方案。在管理方面,浪潮GS 有效帮助企业有效实现财务集中管理、资金集中管理、资产集中管理、供应链集中管理,从而达到集团信息的…

案例|华能某风电场配电房智能巡检机器人解决方案

随着风电产业的迅猛发展&#xff0c;风电场内配电房是风电场电能传输和转换的关键节点&#xff0c;其设备运行状况直接影响到风电场的整体运行效率和安全性。传统的人工巡检方式存在效率低、误差大、安全风险高等问题&#xff0c;难以满足现代风电场对高效、可靠运维的需求。智…

第一百七十二节 Java IO教程 - Java I/O缓冲区

Java IO教程 - Java I/O缓冲区 什么是NIO&#xff1f; 在NIO中&#xff0c;我们处理I/O操作的通道和缓冲区。 像流一样的通道表示数据源/接收器和用于数据传输的Java程序之间的连接。 通道提供双向数据传输设施。我们可以使用通道来读取数据以及写入数据。根据我们的需要&a…

【python】Python常见的面试题解析:深入探索与实践,助你少走弯路

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

Redis分布式系统中的主从复制

本篇文章主要对Redis的主从复制进行讲解。主要分析复制的原理&#xff0c;包括:建立复制、全量复制、部分复制、全量复制、心跳检测等。希望本篇文章会对你有所帮助。 文章目录 一、主从复制简介 二、配置主从复制模式 断开主从复制 安全性 只读 传输延迟 三、拓扑结构 四、主…

秋招复习笔记——八股文部分:网络TCP

TCP 三次握手和四次挥手 TCP 基本认识 序列号&#xff1a;在建立连接时由计算机生成的随机数作为其初始值&#xff0c;通过 SYN 包传给接收端主机&#xff0c;每发送一次数据&#xff0c;就「累加」一次该「数据字节数」的大小。用来解决网络包乱序问题。 确认应答号&#xf…

字符的统计——423、657、551、696、467、535

423. 从英文中重建数字 最初思路 首先要有一个指针&#xff0c;对于3/4/5为一组地跳跃。起初想的是后瞻性&#xff0c;如果符合0-9任意&#xff0c;则更换index、跳跃。此时写了一个函数&#xff0c;用来判断s的截取段和0-9中有无符合。这个思路并没有进行下去&#xff0c;虽然…

C++ - 基于多设计模式下的同步异步⽇志系统

1.项目介绍 项⽬介绍 本项⽬主要实现⼀个⽇志系统&#xff0c; 其主要⽀持以下功能: • ⽀持多级别⽇志消息 • ⽀持同步⽇志和异步⽇志 • ⽀持可靠写⼊⽇志到控制台、⽂件以及滚动⽂件中 • ⽀持多线程程序并发写⽇志 • ⽀持扩展不同的⽇志落地⽬标地 2.开发环境 • Cent…

第二证券:净现比是什么?怎么计算?

1、净现比的含义 净现比是指运营活动产生的现金净流量与净获利的百分比&#xff0c;是用来衡量企业实在盈利才干的目标。净现比也叫做净获利现金含量&#xff0c;即企业的净获利中&#xff0c;有多少转化成现金进入我们的钱包。 2、净现比的核算 净现比的核算公式为&#xf…

聊聊预训练模型的微调

前言 Transformers 提供了一个 Trainer 类&#xff0c;处理微调在数据集上提供的任何预训练模型。 完成所有数据预处理工作后&#xff0c;只需执行几个步骤即可定义 Trainer。 最困难的部分可能是准备运行 Trainer.train() 的环境&#xff0c;因为它在 CPU 上运行速度非常慢。…

全面解析:如何开发智能的食堂采购管理系统

今天&#xff0c;小编将全面解析如何开发一个智能的食堂采购系统&#xff0c;包括需求分析、系统设计、技术选择、开发过程以及测试和部署等方面。 一、需求分析 在开发智能食堂采购系统之前&#xff0c;首先需要进行详细的需求分析。这一步至关重要&#xff0c;因为它直接决定…

【Java 数据结构】ArrayList类介绍

ArrayList类介绍 初识List接口ArrayList类ArrayList类是什么顺序表的模拟实现初始化增加元素删除元素查找元素修改元素 ArrayList类使用构造方法ArrayList源码阅读常用方法及其注意事项 初识List接口 List 是集合框架中的一个接口, 它的里面包含了一些方法, 例如add(), remove…

【Python系列】详解 open 函数:文件操作的基石

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

OpenAI 发完 GPT-4o,国内大模型行业还有哪些机会?

AI视频生成&#xff1a;小说文案智能分镜智能识别角色和场景批量Ai绘图自动配音添加音乐一键合成视频百万播放量 全世界范围内的很多人&#xff0c;我也不例外&#xff0c;想象中的GPT5发布时间应该是24年中&#xff0c;但实际上OpenAI在这个时间点最强的模型是GPT4o&#xff0…