Q-learning解决悬崖问题

强化学习——Q-Leaning算法原理 是用在决策上的一个策略,以概率ε选择随机动作,以概率1-ε选择当前最优动作,比如epsilon = 0.9的时候,就说明百分之90的情况我会按照Q表的最优值选择行为,百分之10的时间随机选择行为。alpha:学习率,决定这次的误差有多少是要被学习的。学习率控制着每次更新Q值时所采用的步长。较大的学习率会导致Q值函数的快速更新,可能会使算法更快地收敛,但也可能导致不稳定性和震荡。如果学习率过大,会导致Q值函数不稳定,可能会导致算法无法收敛或者在局部最优解处震荡。 阅读详情

Q-learning是一个经典的强化学习算法,是一种基于价值(Value-based)的算法,通过维护和更新一个价值表格(Q表格)进行学习和预测。

Q-learning是一种off-policy的策略,也就是说,它的行动策略和Q表格的更新策略是不一样的。

行动时,Q-learning会采用epsilon-greedy的方式尝试多种可能动作。

更新时,Q-learning会采用潜在收益最大化的动作进行价值更新。

总体来说,Q-learning是一个非常勇敢的策略,在有限动作和有限状态情况下,它能够收敛找到全局最优策略。

公众号算法美食屋后台回复关键词:torchkeras,获取本文notebook源代码~

〇,强化学习基本概念

1, 环境(env)和智能体(agent)

在第n步,agent处于状态 state(n)中,然后采取行动action(n),env给agent奖励reward(n+1),同时agent的状态变成 state(n+1)

---reward(n+1), state(n+1)-->
env                              agent(state)
    <------ action(n) ----------

以我们玩一个俄罗斯方块游戏为例。

环境env就是这个游戏背后的程序,智能体agent就是玩家。

假设现在是第n步,state(n)就是目前游戏所处的状态,可以表示为一个矩阵,也就是游戏界面每个格子的明暗状态。

我们可以采取某个 action(n) (向左,向右,向下,变形)。

然后我们会获得一个奖励reward(n),即得分。奖励很多时候是稀疏的,即大部分时候为0,操作很多步才有一个不为0的奖励。

同时游戏界面发生变化,状态由 state(n) 变成 state(n+1)。

2, 马尔科夫交互链

env和agent交互作用若干个步骤,到达结束状态,通常叫做一个episode(片段)。

8edb63a9de035882ae11dc82ba8aeb2c.png

在俄罗斯方块游戏的例子中,一局完整的游戏构成一个马尔科夫交互链,叫做一个episode.

之所以叫做马尔科夫交互链,是因为这个过程满足马尔科夫假设。

第n+1步骤的状态state(n+1)和奖励reward(n+1)只和第n步骤的状态stage(n)和action(n)有关,而与之前的状态和action无关。

马尔科夫假设要求我们在设计state和action的时候,要考虑到所有相关变量。

并且,只要设计出合理的state变量和action变量,任何游戏都可以表示为这样一个马尔科夫交互链。

3, 奖励折现公式

为了衡量每个步骤中action的价值,需要将该步骤之后的奖励,以及未来的全部奖励按照类似金融学中的折现算法求和。

在俄罗斯方块游戏的例子中,一个操作action的价值,不仅跟这个操作完成后立刻获得的奖励reward有关,还要考虑到这个操作的长远影响。

但这种长远影响不太好精确地计算,因为后面获得的奖励,不仅跟当前的action有关,还跟后面的操作有关,所以跟当前操作的相关性是小于1的。

作为简化起见,我们通过类似金融学中现金流折现的方式将未来的奖励全部折算到当前步骤。折算因子gamma一般取值在0.9~1.0之间。

0ffe1984d4771b84db5f588143477358.png

4, epsilon-greedy 学习策略

训练时使用epsilon探索,预测时使用greedy贪心。

训练阶段: 以一定epsilon概率选择随机动作,以(1-epsilon)选择最大化Q(s,a)的动作。

预测阶段: 贪心策略,直接选择最大化Q(s,a)的动作。

为了让模型去探索更优策略,我们在训练过程中会允许模型以一定的概率去实施随机动作,以便评估不同动作的价值。

这样也能够让模型对状态动作空间进行更分散的采样,学到的结果也会更加鲁棒。

但在测试过程,为了获得更好的结果,我们应该采用预期价值最大的动作。

5, Q表格软更新

935f29c256ca4a2865264e736d42784c.png

奖励折现公式对每个action的价值的计算方法是一种粗糙的估计算法。

不同的step或者不同的episode中,按照奖励折现公式对相同state下相同action价值的评估的结果可能差异很大。

为了保持学习过程的稳定性,让Q值不会过分受到某次评估的影响,我们采用一种软更新的方式。

也就是我们在更新Q表格的时候,只让Q值朝着折现公式计算结果靠近一点点(缩小差值),而不是直接调整为折现公式的计算结果。

这样,我们最终的Q表格中action的价值结果相当是许多次不同episode不同step下奖励折现公式计算结果的某种平均值。

一,准备环境

gym是一个常用的强化学习测试环境,可以用make创建环境。

env具有reset,step,render几个方法。

  • 悬崖问题

环境设计如下:

环境一共有48个state状态。

其中T为目标位置,到达目标位置游戏结束。

10个用C表示的为悬崖,掉入悬崖会拉回到起始位置。

智能体设计如下:

智能体有4种动作action,0表示往上,1往右,2往下,3往左。

reward设计如下:

智能体每走一步都会有-1的reward。

这个问题希望训练一个能够尽可能快的从起始位置到达目标位置T的智能体Agent。

import gym
import numpy as np
import time
import matplotlib
import matplotlib.pyplot as plt
from IPython import display

print("gym.__version__=",gym.__version__)


%matplotlib inline

#可视化函数:
def show_state(env, step, info=""):
    plt.figure(num=0,dpi=180)
    plt.clf()
    plt.imshow(env.render())
    plt.title("Step: %d %s" % (step, info))
    plt.axis('off')
    display.clear_output(wait=True)
    display.display(plt.gcf())
    
    
env = gym.make("CliffWalking-v0",render_mode="rgb_array")  # 0 up, 1 right, 2 down, 3 left
env.reset()

for step in range(20):
    time.sleep(0.2)
    
    action = np.random.randint(0, 4)
    obs, reward, done,truncated, info = env.step(action)
    
    #env.render() 
    show_state(env,step=step)
    #print('step {}: action {}, obs {}, reward {}, done {}, truncated {}, info {}'.format(\
    #        step, action, obs, reward, done, truncated,info))
    
display.clear_output(wait=True)

我们先来看看没有训练模型,按照随机的方式会怎么走。

6cb533c2e7af2027650b55b76310f385.gif

二,定义Agent

import torch 
from torch import nn 

class QAgent(nn.Module):
    def __init__(self, obs_n, act_n, learning_rate=0.01, gamma=0.9, e_greed=0.1):
        super().__init__()
        self.act_n = act_n      # 动作维度,有几个动作可选
        self.lr = learning_rate # 学习率
        self.gamma = gamma      # reward的衰减率
        self.epsilon = e_greed  # 按一定概率随机选动作
        self.Q = nn.Parameter(torch.zeros((obs_n, act_n)),requires_grad=False)

    # 根据输入观察值,采样输出的动作值,带探索
    def sample(self, obs):
        if np.random.uniform(0, 1) < (1.0 - self.epsilon): #根据table的Q值选动作
            action = self.predict(obs)
        else:
            action = np.random.choice(self.act_n) #有一定概率随机探索选取一个动作
        return action
    
    # 根据输入观察值,预测输出的动作值
    def forward(self,obs):
        Q_list = self.Q[obs, :]
        maxQ = Q_list.max()
        action_list = torch.where(Q_list == maxQ)[0].tolist()  # maxQ可能对应多个action
        action = np.random.choice(action_list)
        return action

    
    @torch.no_grad()
    def predict(self,obs):
        self.eval()
        return self.forward(obs)
    

    # 学习方法,也就是更新Q-table的方法
    def learn(self, obs, action, reward, next_obs, done):
        """ on-policy
            obs: 交互前的obs, s_t
            action: 本次交互选择的action, a_t
            reward: 本次动作获得的奖励r
            next_obs: 本次交互后的obs, s_t+1
            next_action: 根据当前Q表格, 针对next_obs会选择的动作, a_t+1
            done: episode是否结束
        """
        predict_Q = self.Q[obs, action]
        if done:
            target_Q = reward # 没有下一个状态了
        else:
            target_Q = reward + self.gamma * self.Q[next_obs, :].max() # Q-learning
        self.Q[obs, action] += self.lr * (target_Q - predict_Q) # 修正q

我们创建一下env和agent.

# 使用gym创建悬崖环境
env = gym.make("CliffWalking-v0")  # 0 up, 1 right, 2 down, 3 left

# 创建一个agent实例,输入超参数
agent = QAgent(
        obs_n=env.observation_space.n,
        act_n=env.action_space.n,
        learning_rate=0.1,
        gamma=0.9,
        e_greed=0.1)

三,训练Agent

下面我们将套用torchkeras的训练模版来对Agent进行训练。

由于强化学习问题与常用的监督学习范式有很大的差异,所以我们对torchkeras的训练模版在

StepRunner, EpochRunner这2个层级上都有少量的修改。

class DataLoader:
    def __init__(self,env,agent,stage='train'):
        self.env = env
        self.agent = agent 
        self.stage = stage
        
    def __iter__(self):

        obs,info = self.env.reset() # 重置环境, 重新开一局(即开始新的一个episode)
        action = self.agent.sample(obs) # 根据算法选择一个动作

        while True:
            next_obs, reward, done, _, _ = self.env.step(action) # 与环境进行一个交互
            if self.stage =='train':
                next_action = self.agent.sample(next_obs) # 训练阶段使用探索-利用策略
            else:
                next_action = self.agent.predict(next_obs) # 验证阶段使用模型预测结果

            yield obs, action, reward, next_obs, done
            action = next_action
            obs = next_obs   

            if done:
                break

dl_train = DataLoader(env,agent,stage='train')
dl_train.size = 1000

dl_val = DataLoader(env,agent,stage='val')
dl_val.size = 200
import sys,datetime
from tqdm import tqdm
import numpy as np

from accelerate import Accelerator
from torchkeras import KerasModel
import pandas as pd 
from torchkeras.utils import is_jupyter,colorful

from copy import deepcopy

class StepRunner:
    def __init__(self, net, loss_fn, accelerator=None, stage = "train", metrics_dict = None, 
                 optimizer = None, lr_scheduler = None
                 ):
        self.net,self.loss_fn,self.metrics_dict,self.stage = net,loss_fn,metrics_dict,stage
        self.optimizer,self.lr_scheduler = optimizer,lr_scheduler
        self.accelerator = accelerator if accelerator is not None else Accelerator()
    
    def __call__(self, batch):
        
        obs, action, reward, next_obs, done = batch
        
        #backward()
        if self.stage=="train":
             self.net.learn(obs, action, reward, next_obs, done)
            
        #losses (or plain metric)
        step_losses = {self.stage+'_reward':reward, 
                       self.stage+'_done':1.0 if done else 0.0}
        
        #metrics (stateful metric)
        step_metrics = {}
        if self.stage=='train':
            step_metrics['lr'] = self.net.lr
        return step_losses,step_metrics
    

class EpochRunner:
    def __init__(self,steprunner,quiet=False):
        self.steprunner = steprunner
        self.stage = steprunner.stage
        self.accelerator = steprunner.accelerator
        self.net = steprunner.net
        self.quiet = quiet
        
    def __call__(self,dataloader):
        dataloader.agent = self.net 
        n = dataloader.size  if hasattr(dataloader,'size') else len(dataloader)
        loop = tqdm(enumerate(dataloader,start=1), 
                    total=n,
                    file=sys.stdout,
                    disable=not self.accelerator.is_local_main_process or self.quiet,
                    ncols=100
                   )
        epoch_losses = {}
        for step, batch in loop: 
            step_losses,step_metrics = self.steprunner(batch)   
            step_log = dict(step_losses,**step_metrics)
            for k,v in step_losses.items():
                epoch_losses[k] = epoch_losses.get(k,0.0)+v

            if step_log[self.stage+'_done']<1 and step<n:
                loop.set_postfix(**step_log)



            elif step_log[self.stage+'_done']>0.5 or step==n:
                epoch_metrics = step_metrics
                epoch_metrics.update({self.stage+"_"+name:metric_fn.compute().item() 
                                 for name,metric_fn in self.steprunner.metrics_dict.items()})
                epoch_losses = {k:v for k,v in epoch_losses.items()}
                epoch_log = dict(epoch_losses,**epoch_metrics)
                epoch_log[self.stage+'_step']=step
                loop.set_postfix(**epoch_log)



                for name,metric_fn in self.steprunner.metrics_dict.items():
                    metric_fn.reset()
                loop.close()
            else:
                break
        return epoch_log
    
KerasModel.StepRunner = StepRunner
KerasModel.EpochRunner = EpochRunner
keras_model = KerasModel(net= agent,loss_fn=None)
dfhistory = keras_model.fit(train_data = dl_train,
    val_data=dl_val,
    epochs=600,
    ckpt_path='checkpoint.pt',
    patience=500,
    monitor='val_reward',
    mode='max',
    callbacks=None,
    quiet=True, 
    plot=True,
    cpu=True)

42024a55d73ad2515bf3177c8e95f7a2.png

dfhistory['val_reward'].max()
-13.0
keras_model.load_ckpt('checkpoint.pt')
agent = keras_model.net

四,测试Agent

def test_agent(env, agent):
    total_reward = 0
    obs,info = env.reset()
    step=0
    while True:
        action = agent.predict(obs) # greedy
        next_obs, reward, done, _ ,_ = env.step(action)
        total_reward += reward
        obs = next_obs
        time.sleep(0.5)
        show_state(env,step)
        step+=1
        if done:
            break
    plt.close()
    return total_reward
# 全部训练结束,查看算法效果
env = gym.make("CliffWalking-v0",render_mode="rgb_array")  # 0 up, 1 right, 2 down, 3 left
test_reward = test_agent(env, agent)
print('test reward = %.1f' % (test_reward))

746c24efe3dbb452fd9f0e031bd2c9eb.gif

test reward = -13.0

可以看到,训练完成后,这个agent非常机智地在悬崖边上走了一个最优路线,但却没有掉到悬崖里去。😊

五,保存Agent

torch.save(keras_model.net.state_dict(),'best_ckpt.pt')

公众号算法美食屋后台回复关键词:torchkeras,获取本文notebook源代码以及更多有趣范例。

f6af7f07c0265eea6da918441c918663.png

深度增强学习——Q-learning和决策梯度 深度增强学习——Q-learning和决策梯度 一、什么是强化学习 强化学习是指,我们有一个智能体(agent),能够在其环境(environment)中采取行动,也可以因为其行动获得奖励,它的目标是学会如何行动以最大限度地获得奖励。强化学习多是一种动态规划的思路,使用生活化语言描述,就叫做:实践出真知。与之前学过的监督学习和无监督学习不同,强化学习本身并不依赖于数据或者数据的标签,而是依赖于对输入数据预测之后的反馈,因此它介于监督学习和非监督学习之间。 如上图所示,一个agent(例如:玩家)做出了一个 阅读详情

相关推荐

深度强化学习-Q-learning解决悬崖寻路问题-笔记(三)

Q-learning解决悬崖寻路问题一级目录二级目录三级目录 一级目录 二级目录 三级目录

wield_jjz的博客 4532

Q-Learning

Q-learning原理即代码实现

帅帅气气的黑猫警长 2万+

利用Q-learning解决Cliff-walking问题

在写的过程中参考了大量其他人所写的算法教程,因为看太多了,我也不太记得参考过谁的了,在此统一感谢了! 格式排版有点难整,和word不太一样,大家将就的看吧。。。

菜狗的玄学修炼室 6108

【机器学习】Q-Learning详细介绍

Q-learning 是一种机器学习方法,它使模型能够通过采取正确的操作来迭代学习和改进。 Q-learning属于强化学习的算法

qq_43774332的博客 1万+

Q-Learning 简介:初学者教程(1)

强化学习强调无模型学习算法,因此出现Q-Learning,Q-Learning算法酷似“有限状态自动机”模型,只是增加了奖励机制和Agent机制,而Agent与粒子群算法、蒙特卡洛算法是有关的。本文介绍这个算法框架。

gongdiwudu的专栏 4668

强化学习 案例 悬崖寻路 python实现

强化学习 案例 悬崖寻路 python实现

强化学习--项目1使用Q-learning解决悬崖寻路问题

使用Q-learning解决悬崖寻路问题

weixin_43557816的博客 917

低成本实现webhook接收端[python]

Webook本质上也是API,只不过是反向调用,即前端不主动发送请求,完全由后端推送。简单来说,Webhook是一个接受HTTP POST或是GET,PUT,DELETE的URL,一个实现了Webhook的API提供商就是在当事件发生的时候会向配置的URL发送信息,与请求-响应式不同,使用Webhook可以实时接收到变化。Ps:Webhook可能会发出大量的请求,确保接受的URL不被泄露或者应用能及时处理这些请求。

锡城小凯的博客 1万+

【限时免费】 Easy-RL项目:Q-learning算法解决悬崖寻路问题详解

Easy-RL项目:Q-learning算法解决悬崖寻路问题详解 【免费下载链接】easy-rl 强化学习中文教程(蘑菇书????),在线阅读地址:https://datawhalechina.github.io/easy-rl/ 项目...

gitblog_00053的博客 520

Hexo博客使用PDF.js

clone了一个pdf.js 然后兴高采烈地到文章里使用{% pdf %}标签 结果浏览器弹出了个下载框(使用GitHub的raw) 不知道是不是因为使用GitHub的原因导致这个问题 后来我测试的时候报了个错 优快云上的解决方法 到时候我再试试把资源放回lib/里面看看行不行 目录1.clone1.1.官方方法1.2.NexT主题方法1.3.补充2.修改文件2.1.修改viewer.htm...

LTFan 963

线性自抗扰控制LADRC(一)---基础知识

LADRC(Linear Active Disturbance Rejection Control)是ADRC的线性化版本,通过简化非线性模块(如ESO和反馈律)降低计算复杂度,适用于实时控制系统(如电机驱动)。

ApexYuan 2078

强化学习之Q-learning与Sarsa算法解决悬崖寻路问题

之前有写过利用Q-learning算法去解决-> 一维二维探宝游戏:https://blog.youkuaiyun.com/MR_kdcon/article/details/109612413 有风格子寻路游戏:https://blog.youkuaiyun.com/MR_kdcon/article/details/110600819 理论与实践都证明:Q-learning对于解决状态有限、离散的RL任务有着不错的收敛效果。Q-learning是off-policy算法,意思是其行为策略与目标策略是独立的,根据这个特点

Ton的博客 7350

Android Studio打包全攻略---从入门到精通

初出茅庐 手动打包怎么手动打包项目写完了,现在需要把应用上传到市场,问题出现—怎么把代码变成.apk(Android的可安装文件)。 1. 创建签名文件 2. 填写好签名参数 3. 生成APK 注意:签名的密码和密匙的密码注意保管,不要忘了,签名文件别泄漏了,也别搞丢了为什么要打包我最开始就有这个疑问,我们的代码不是点了下运行按钮就直接安装到手机上了吗,我们在在项目Project

我早就想,要这样载着你,翻山越岭 2万+

强化学习实战-使用Q-learning算法解决悬崖问题

Q-learning简介 Q-learning也是采用Q表格的方式存储Q值(状态动作价值),决策部分与Sarsa是一样的,采用ε-greedy方式增加探索。 Q-learning跟Sarsa不一样的地方是更新Q表格的方式。 Sarsa是on-policy的更新方式,先做出动作再更新。 Q-learning是off-policy的更新方式,更新learn()时无需获取下一步实际做出的动作next_action,并假设下一步动作是取最大Q值的动作。 Q-learning的更新公式: 悬崖问题 找到绕过悬崖

午夜的行人 1427

【WRF-UCM】BEP+BEM 模拟:README

【WRF-UCM】BEP+BEM 模拟:README

WW、forever的博客 223

OpenClaw使用ClawHub安装skill网络不稳定?Rate limit exceeded报错?试试这个国内高速镜像工具!

使用 ClawHub 安装 Skill 时遇到 🌐 网络不稳、❌ Rate limit 或命令不可用?使用这个镜像工具,轻松解决skill安装问题,⚡

XiaoqiangClub的博客 1261

人工智能基础部分23-利用Q-Learning解决“悬崖行走”与迷宫问题的案例

大家好,我是微学AI,今天给大家介绍一下人工智能基础部分23-利用Q学习解决“悬崖行走”与迷宫问题的案例。Q学习(Q-Learning)是一种无模型的强化学习算法,它通过Q表来评估在给定状态下采取某一动作的期望回报。Q表是一个表格,其行代表状态,列代表动作,每个单元格的值代表在某一状态下采取某一动作的Q值。

微学AI的博客 1636

8.19华为OD机试真题 新系统 - 基站低能耗时段统计 (Java/Py/C/C++/Js/Go)

8.19华为OD机试真题 新系统 - 基站低能耗时段统计 (Java/Py/C/C++/Js/Go)

算法大师! 1153

时序差分学习实战:用Sarsa和Q-learning解决悬崖寻路问题(附完整代码)

本文通过经典的悬崖寻路问题,对比分析了时序差分学习中的Sarsa和Q-learning算法。Sarsa作为同策略算法,学习到保守的安全路径;而Q-learning作为异策略算法,则倾向于学习理论最优但风险更高的悬崖边缘路径。文章提供了完整的Python代码实现和可视化分析,深入探讨了探索与利用的权衡,并介绍了期望Sarsa等变体,为强化学习实践者提供了宝贵的算法选择洞见。

year5的博客 183

在Android Jetpack Compose中实现夜间模式

Android基于Compose的深色主题实现

虎哥LoveDroid 1242

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

【强化学习】悬崖寻路:Sarsa和Q-Learning

悬崖寻路:Sarsa和Q-Learning

兴趣使然的创作者 3499

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

【零基础强化学习】100行代码教你训练——基于Q-learning的CliffWalking爬悬崖游戏

【零基础强化学习】100行代码教你训练——基于Q-learning的CliffWalking爬悬崖游戏,代码亲自跑通,一起交流学习呀 q-learning下一步的Q对应的action是直接选取最大值,不是实际与环境交互的动作(==属于off-policy==),只选最大值的总动作意味着只关心高奖励的状态,低奖励影响不大,所以实验结果贴着障碍物走,**很大胆**!

南城果宝的满腹经纶 3593

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

悬崖寻路问题Sarsa和QLearning实现

Sarsa更新速度慢,但较为安全;QLearning速度快,但容易掉进悬崖。从对比效果来看,QLearning速度远快于Sarsa。

qq_43773207的博客 873

【Sarsa&Q-learning】表格型强化学习方法

Sarsa算法和Q-learning算法是两种基于表格的经典强化学习方法,本文将对比探究这两种方法在解决悬崖行走(CliffWalking)问题时的表现。

Kevin Tang的博客 3527

基础的强化学习(RL)算法及代码详细demo

基础的强化学习算法及代码详细demo

weixin_45696231的博客 1万+

【学习强化学习】三、Q learning和Sarsa算法

文章目录参考资料1. Q- table2. Model-free Prediction2.1 Monte-Carlo Policy Evaluation2.1.1 MC算法步骤2.1.2 incremental MC updates2.1.3 Difference between DP and MC for policy evaluation2.1.4 Advantages of MC over DP2.2 Temporal Difference2.2.1 TD 方法介绍2.2.2 Advantages o

weixin_42301220的博客 2004
上一篇: 10种实用的Prompt技巧图解
下一篇: 30分钟吃掉DQN算法
算法美食屋
博客等级 码龄9年 2万+粉丝 154原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值