2024年9月

说明

该文章是属于OverallAuth2.0系列文章,每周更新一篇该系列文章(从0到1完成系统开发)。

该系统文章,我会尽量说的非常详细,做到不管新手、老手都能看懂。

说明:OverallAuth2.0 是一个简单、易懂、功能强大的权限+可视化流程管理系统。

结合上一篇文章使用,味道更佳:
.net core8 使用Swagger(附当前源码)

有兴趣的朋友,请关注我吧(*^▽^*)。

第一步:安装最新Jwt包

包名:Microsoft.AspNetCore.Authentication.JwtBearer

第二步:appsettings.json中配置jwt

 /*jwt鉴权*/
 "JwtSetting": {"Issuer": "微信公众号:不只是码农", //发行人
   "Audience": "微信公众号:不只是码农", //订阅人
   "ExpireSeconds": 120, //过期时间,默认分钟
   "ENAlgorithm": "HS256", //秘钥算法
   "SecurityKey": "bzsmn=Start20240913EndOverallAuth-WebApi" //秘钥构成
 }

第三步:创建jwt解析模型

在OverallAuth-WebApi项目的目录下创建文件夹【model】,并创建一个类文件JwtSettingModel.cs

OverallAuth-WebApi结构,见上一篇文章:
.net core8 使用Swagger(附当前源码)

 /// <summary>
 ///jwt配置模型/// </summary>
 public classJwtSettingModel
{
/// <summary> ///密钥/// </summary> public string SecurityKey { get; set; }/// <summary> ///加密算法/// </summary> public string ENAlgorithm { get; set; }/// <summary> ///颁发者/// </summary> public string Issuer { get; set; }/// <summary> ///接收者/// </summary> public string Audience { get; set; }/// <summary> ///过期时间 单位:秒/// </summary> public int ExpireSeconds { get; set; }
}

目录结构如下:

第四步:创建Jwt、AppSettings插件

目录结构如下:

上图可以看到,我们创建了JwtPlugInUnit和AppSettingsPlugInUnit2个插件,它分别对应jwt和AppSettings配件文件的解析。

那么我们看下,这2个类里面的具体内容。

JwtPlugInUnit如下:

/// <summary>
///jwt插件/// </summary>
public static classJwtPlugInUnit
{
/// <summary> ///初始化JWT/// </summary> /// <param name="services"></param> public static void InitJWT(thisIServiceCollection services)
{
var jwtsetting = AppSettingsPlugInUnit.GetNode<JwtSettingModel>("JwtSetting");
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(o
=>{
o.TokenValidationParameters
= newTokenValidationParameters()
{
ValidateIssuerSigningKey
= true,
ValidIssuer
=jwtsetting.Issuer,
ValidAudience
=jwtsetting.Audience,
IssuerSigningKey
= newSymmetricSecurityKey(Encoding.ASCII.GetBytes(jwtsetting.SecurityKey)),
ValidateIssuer
= true,
ValidateAudience
= true,
ValidateLifetime
= true,
ClockSkew
=TimeSpan.Zero
};
});
}
}

AppSettingsPlugInUnit如下:

 /// <summary>
 ///AppSettings配置文件插件/// </summary>
 public classAppSettingsPlugInUnit
{
/// <summary> ///声明配置属性/// </summary> public static IConfiguration Configuration { get; set; }/// <summary> ///构造函数/// </summary> staticAppSettingsPlugInUnit()
{
Configuration
= newConfigurationBuilder()
.Add(
new JsonConfigurationSource { Path = "appsettings.json", ReloadOnChange = true})
.Build();
}
/// <summary> ///获得配置文件的对象值/// </summary> /// <param name="jsonPath">文件路径</param> /// <param name="key"></param> /// <returns></returns> public static string GetJson(string jsonPath, stringkey)
{
if (string.IsNullOrEmpty(jsonPath) || string.IsNullOrEmpty(key)) return null;
IConfiguration config
= new ConfigurationBuilder().AddJsonFile(jsonPath).Build();//json文件地址 return config.GetSection(key).Value;//json某个对象 }/// <summary> ///获取数据库连接字符串/// </summary> /// <returns></returns> public static stringGetMysqlConnection()
{
return Configuration.GetConnectionString("MySql").Trim();
}
/// <summary> ///根据节点名称获取配置模型/// </summary> /// <typeparam name="T"></typeparam> /// <param name="Node"></param> /// <returns></returns> public static T GetNode<T>(string Node) where T : new()
{
T model
= Configuration.GetSection(Node).Get<T>();returnmodel;

}
}

第五步:让jwt遵守Swagger协议

因为我们系统使用到了Swagger,所以要让jwt遵守Swagger协议,因此我们要在Swagger中添加如下代码。

/// <summary>
///初始化Swagger/// </summary>
/// <param name="services"></param>
public static void InitSwagger(thisIServiceCollection services)
{
//添加swagger services.AddSwaggerGen(optinos =>{typeof(ModeuleGroupEnum).GetEnumNames().ToList().ForEach(version =>{
optinos.SwaggerDoc(version,
newOpenApiInfo()
{
Title
= "权限管理系统",
Version
= "V2.0",
Description
= "求关注,求一键三连",
Contact
= new OpenApiContact { Name = "微信公众号作者:不只是码农 b站作者:我不是码农呢", Url = new Uri("http://www.baidu.com") }
});

});
//反射获取接口及方法描述 var xmlFileName = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
optinos.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, xmlFileName),
true);

//使用jwt
optinos.AddSecurityDefinition(
"Bearer", newOpenApiSecurityScheme
{
Description
= "请在下方输入框子输入Bearer Token 开启JWT鉴权",
Name
= "Authorization", //默认名称,不能修改 In =ParameterLocation.Header,
Type
=SecuritySchemeType.ApiKey,
Scheme
= "Bearer"});//让swagger遵守jwt协议 optinos.AddSecurityRequirement(newOpenApiSecurityRequirement
{
{
newOpenApiSecurityScheme
{
Reference
= newOpenApiReference
{
Type
=ReferenceType.SecurityScheme,
Id
= "Bearer"}
},
new List<string>()
}
});

});
}

说明:InitSwagger方法是初始化Swagger的方法,在上一篇文章:
.net core8 使用Swagger(附当前源码)
中有讲到。

第六步:初始化Jwt

在Program中添加一下代码,初始化Jwt

第七步:验证Jwt

做好以上步骤,jwt就可以正常使用。

当你看到图中标识时,就表示jwt初始化成功,就可以在系统中使用jwt鉴权等操作。

使用【[Authorize]】、【 [AllowAnonymous]】特性测试鉴权。

以下2个接口一个需要验证、一个不需要验证,我们来测试下。

[Authorize]开启验证测试

CheckJwt接口:开启验证,不传token

可以看到,开启jwt验证的,接口在没有传入token的情况下,访问失败。

UnCheckJwt接口:不开启验证。

以上就是.net core8 使用jwt系统鉴权的配置过程。

感谢你的耐心观看。

如果对你有帮助,请关注我微信公众号吧(*^▽^*)。

源代码地址:https://gitee.com/yangguangchenjie/overall-auth2.0-web-api

帮我Star,谢谢。

有兴趣的朋友,请关注我微信公众号吧(*^▽^*)。

关注我:一个全栈多端的宝藏博主,定时分享技术文章,不定时分享开源项目。关注我,带你认识不一样的程序世界

无数据知识蒸馏能够利用大型教师网络所学到的知识,来增强较小型学生网络的训练,而无需访问原始训练数据,从而避免在实际应用中的隐私、安全和专有风险。在这方面的研究中,现有的方法通常遵循一种反演蒸馏的范式,在预训练教师网络指导下实时训练生成对抗网络来合成一个大规模的样本集用于知识蒸馏。论文重新审视了这种常见的无数据知识蒸馏范式,表明通过“小规模逆向数据进行知识蒸馏”的视角,整体训练效率有相当大的提升空间。根据三个经验观察结果,这些观察结果表明在数据逆向和蒸馏过程中平衡类别分布在合成样本多样性和难度上的重要性,论文提出了小规模无数据知识蒸馏(
SSD-KD
)。在形式化上,
SSD-KD
引入了一个调节函数来平衡合成样本,并引入了一个优先采样函数来选择合适的样本,通过动态回放缓冲区和强化学习策略进行辅助。
SSD-KD
可以在极小规模的合成样本(例如,比原始训练数据规模小10
\(\times\)
)条件下进行蒸馏训练,使得整体训练效率比许多主流方法快一个或两个数量级,同时保持卓越或竞争性的模型性能,这一点在流行的图像分类和语义分割基准上得到了验证。

来源:晓飞的算法工程笔记 公众号,转载请注明出处

论文: Small Scale Data-Free Knowledge Distillation

Introduction


对于资源受限设备上的计算机视觉应用,如何学习便携的神经网络同时保持满意的预测准确性是关键问题。知识蒸馏(
KD
)利用预训练的大型教师网络的信息来促进在相同训练数据上的较小目标学生网络的训练,已经成为一种主流解决方案。传统的
KD
方法假设原始训练数据始终可用。然而,由于潜在的隐私、安全、专有或数据规模等问题,实际中通常无法访问教师网络训练时使用的源数据集。为了解决训练数据的限制,数据无关的知识蒸馏最近引起了越来越多的关注。

数据无关知识蒸馏(
D-KD
)的基本思想是基于预训练的教师网络构建合成样本进行知识蒸馏,这些合成样本应当匹配原始训练数据的潜在分布。现有的顶级
D-KD
方法通常采用对抗性反演和蒸馏范式。

  1. 在反演过程中,通过将预训练的教师网络作为判别器来训练生成器。
  2. 在随后的知识蒸馏过程中,实时学习的生成器将合成伪样本用于训练学生网络。

然而,对抗性
D-KD
方法通常需要生成大量的合成样本(与原始训练数据集的规模相比),以确保可信的知识蒸馏。这给训练资源消耗带来了沉重负担,抑制了其在实际应用中的使用。在最近的一项工作中,作者提出了一种有效的元学习策略,该策略寻找共同特征并将其作为初始先验以减少生成器收敛所需的迭代步骤。尽管可以实现更快的数据合成,但仍然需要生成足够多的合成样本以确保有效的知识蒸馏。这忽略了后续知识蒸馏过程的效率,成为整体训练效率的主要瓶颈。总而言之,目前尚未有研究工作通过共同考虑数据反演和知识蒸馏过程来提高
D-KD
的整体训练效率。

为了弥补这一关键差距,论文提出首个完全高效的
D-KD
方法,即小规模无数据知识蒸馏(
SSD-KD
)。从一种新颖的“小数据规模”视角出发,提高了对抗反演和蒸馏范式的整体训练效率。在这项工作中,“数据规模”指的是在一个训练周期内用于知识蒸馏的总反演样本数量。

SSD-KD
的提出主要源于三个观察的启发。

首先,论文观察到,当在不同的教师-学生网络对上将合成样本和原始样本的数据规模显著缩减到相同水平(例如,源训练数据集大小的
10%
)时,训练于合成样本的学生网络相比于训练于原始样本的对应网络显示出更好的性能,如图
1
所示。需要注意的是,合成样本是在全源数据集上预训练的教师网络的指导下生成的,这自然反映了原始数据分布的不同视角。在足够小的数据规模下,这使得合成样本在拟合整个源数据集的基础分布方面优于原始样本。这一启发性的观察表明,如果能够构建一个小规模的高质量合成样本集,将创造出一种通向完全高效
D-KD
的有希望的方法。从原则上讲,论文认为一个高质量的小规模合成数据集应该在合成样本的多样性和难度方面具有良好的类别分布平衡。

然而,论文其他两个观察表明,现有的
D-KD
方法,包括传统的和最有效的设计,都没有在小数据规模下平衡上述两个方面的类别分布的良好能力,如图
2
所示。需要注意的是,已经存在一些
D-KD
方法来增强合成样本的多样性,但合成样本在样本难度方面的多样性尚未被探讨。

基于上述观察和分析,论文提出了
SSD-KD
,引入了两个相互依赖的模块以显著提高主要的对抗性反演和蒸馏范式的整体训练效率。
SSD-KD
的第一个模块依赖于一种新颖的调制函数,该函数定义了一个多样性感知项和一个难度感知项,以明确的方式共同平衡合成样本在数据合成和知识蒸馏过程中的类别分布。第二个模块定义了一种新颖的优先采样函数,这一函数通过强化学习策略来实现,从候选样本中选择少量适当的合成样本,这些样本存储在一个动态重放缓冲区中用于知识蒸馏,从而进一步提高端到端的训练效率。得益于这两个模块,
SSD-KD
具有两个吸引人的优点。一方面,
SSD-KD
可以在极小规模的合成样本(比原始训练数据规模小
10
倍)上进行蒸馏训练,使得整体训练效率比许多主流的
D-KD
方法快一个或两个数量级,同时保持竞争力的模型性能。另一方面,
SSD-KD
在学生模型准确性方面获得了大幅度的提升,并且在将合成样本的数据规模放宽到相对较大的数量时(仍然小于现有
D-KD
方法的规模)保持了整体训练效率。

Method


Preliminaries: D-KD


\({f_t(\cdot;\theta_t)}\)
为一个在原始任务数据集上预训练的教师模型,而该数据集现在已不可访问。
D-KD
的目标是首先通过反演教师模型学到的数据分布信息来构建一组合成训练样本
\(x\)
,然后在这些样本上训练一个目标学生模型
\({f_s(\cdot;\theta_s)}\)
,迫使学生模型模拟教师的功能。现有的
D-KD
方法大多使用生成对抗网络
\(g(;\theta_g)\)
来生成合成训练样本
\(x=g(z;\theta_g)\)
,其中
\(z\)
为潜在噪声输入,该生成对抗网络的训练是通过将教师模型作为鉴别器来进行的。

D-KD
的优化包含一个常见的蒸馏正则化项,用于最小化教师-学生模型之间的功能差异
\(\mathcal{L}_{\text{KD}}({x})=D_\text{KD}(f_t(x; \theta_t)\| f_s(x; \theta_s))\)
,该差异基于
KL
散度。此外,还包含一个任务导向的正则化项
\(\mathcal{L}_{\text{Task}}({x})\)
,例如,使用教师模型的预测作为真实标签的交叉熵损失。除此之外,由于
D-KD
主要基于教师模型经过预训练后能够捕捉源训练数据分布的假设,近年来的
D-KD
方法引入了额外的损失项,以正则化数据反演过程中训练数据分布的统计量(批量归一化(
BN
)参数)。

\[\begin{equation}
\mathcal{L}_{\text{BN}}({x})= \sum_l \big\|\mu_l(x)-\mathbb{E}(\mu_l)\big\|_2+\big\| \sigma_l^2(x)-\mathbb{E}(\sigma_l^2)\big\|_2,
\end{equation}
\]

其中,
\(\mu_l(\cdot)\)

\(\sigma_l(\cdot)\)
分别表示第
\(l\)
层特征图的批量均值和方差估计;
\(\mathbb{E}(\cdot)\)
对批量归一化(
BN
)统计量的期望可以通过运行均值或方差进行近似替代。

D-KD
的有效性在很大程度上依赖于从利用预训练教师模型知识反演得到的合成样本的质量。现行的对抗性
D-KD
范式包含两个过程,即数据反演和知识蒸馏。从效率和效果两个角度来看,一方面,数据反演过程在很大程度上影响学生模型的优化性能;另一方面,知识蒸馏的训练时间成本成为
D-KD
整体训练效率的一个重要限制因素。

Our Design: SSD-KD

SSD-KD
专注于通过“用于知识蒸馏的小规模反演数据”这一视角来改进对抗性
D-KD
范式,着重于利用预训练教师模型和知识蒸馏过程的反馈来指导数据反演过程,从而显著加快整体训练效率。根据前述中的符号,
SSD-KD
的优化目标定义为

\[\begin{align}
\min\limits_{f_s} \max\limits_{g} \mathbb{E}_{x = \delta \circ g\left(z\right)}\big(\mathcal{L}_{\text{BN}}({x})+\mathcal{L}_{\text{KD}}({x})+\phi(x)\mathcal{L}_{\text{Task}}({x})\big),
\label{eq:loss}
\end{align}
\]

其中,

  1. 采用了一种多样性感知的调制函数
    \(\phi(x)\)
    ,该函数根据教师模型对每个合成样本的预测类别分配不同的优先级。


  2. BN
    估计的约束下,通过
    \(\phi(x)\)
    鼓励生成器尽可能探索对教师模型来说更具挑战性的合成样本。

  3. 不采用随机采样策略来选择样本进行知识蒸馏,而是采用重加权策略来控制采样过程。符号
    \(\circ\)
    来表示应用基于优先级采样函数
    \(\delta\)

  4. 每个合成样本不仅由其调制函数
    \(\phi(x)\)
    进行优先级排序,而且在采样阶段还会重新加权,该阶段重用了与
    \(\phi(x)\)
    相同的中间值。

虽然
D-KD
流水线允许合成训练样本并用于在同一任务上训练学生模型,但存在大量数据冗余,这妨碍了
D-KD
方法的训练效率。
SSD-KD
是一种完全高效的
D-KD
方法,能够使用极小规模的合成数据,同时与现有的
D-KD
方法相比,达到具有竞争力的性能。

SSD-KD
的流程总结在算法
1
中,现有对抗性
D-KD
方法的优化流程(包括传统家族和更高效家族)与我们的
SSD-KD
的比较如图
3
所示。

Data Inversion with Distribution Balancing


2
展示了
D-KD
中由于合成数据的大量不平衡导致的数据冗余。右侧两个子图描绘了教师模型预测的类别分布,显示了数据类别的显著不平衡,而左侧两个子图展示了不同预测难度下的样本数量(难度通过教师模型预测的概率来衡量)。对于
D-KD
来说,这表明仅仅根据教师-学生差异来生成样本会导致样本难度分布非常不均,并倾向于获得容易预测的数据样本。对于数据样本全部合成的
D-KD
任务,数据生成过程需要同时考虑教师-学生差异和教师自身的预训练知识,基于此,
SSD-KD
提出了考虑多样性和难度的数据合成方法。

  • Diversity-aware balancing

首先在数据反演过程中解决样本难度不平衡的问题。具体来说,维护一个回放缓冲区
\(\mathcal{B}\)
,它存储一定数量(记作
\(|\mathcal{B}|\)
)的合成数据样本。对于
\(\mathcal{B}\)
中的每个数据样本
\(x\)
,惩罚与
\(x\)
共享相同预测类别(由教师模型预测)的样本总量。为实现这一点,采用了一个考虑多样性的平衡项,该项鼓励生成器合成具有不常见类别的样本。

  • Difficulty-aware balancing

借鉴在对象检测领域中使用焦点损失来处理高度不平衡样本的经验,进一步对每个样本
\(x\)
引入了一个基于预测概率
\(p_T(x)\)
的难度感知平衡项。在这里,困难的合成样本被认为是那些由教师模型预测信心较低的样本,这些样本会受到难度感知平衡项的鼓励。

总之,论文引入了一个调节函数
\(\phi(x)\)
来根据来自预训练教师模型的预测反馈调整生成器的优化。
\(\phi(x)\)
旨在平衡类别分布,并动态地区分容易和困难的合成样本,从而使容易的样本不会过度主导蒸馏过程。形式上,对于合成数据样本
\(x\in\mathcal{B}\)
,其调节函数
\(\phi(x)\)
的计算公式为

\[\begin{equation}
\phi (x) = \underbrace {\Big(1 - \frac{1}{|\mathcal{B}|}\sum_{x'\in\mathcal{B}}\mathbb{I}_{c_T(x')=c_T(x)}\Big)}_{\text{diversity-aware balancing}} \underbrace{{\Big(1 - p_T(x)\Big)}^\gamma }_{\text{difficulty-aware balancing}}
\label{eq:phi}
\end{equation}
\]

其中,
\(c_T(x)\)

\(p_T(x)\)
分别指代预训练教师模型预测的类别索引和概率(或置信度);
\(\mathbb{I}_{c_T(x')=c_T(x)}\)
表示一个指示函数,如果
\(x'\)
的预测类别与
\(x\)
相同,则该函数等于
\(1\)
,否则为
\(0\)

\(\gamma\)
是一个超参数。

强调调节函数
\(\phi(x)\)
的两个特性:

  1. 对于在教师模型中预测置信度较高的数据样本
    \(x\)
    (即被视为容易样本),
    \(\phi(x)\)
    接近于低值,从而对任务相关损失
    \(\mathcal{L}_{\text{Task}}({x})\)
    在公式 (`ref`{eq:loss}) 中的影响较小。
  2. 当教师网络预测的合成数据样本在
    \(\mathcal{B}\)
    中的类别分布严重不平衡时,与
    \(\mathcal{B}\)
    中更多样本共享类别的样本
    \(x\)
    会受到惩罚,从而
    \(\phi(x)\)
    会削弱相应的
    \(\mathcal{L}_{\text{Task}}({x})\)

尽管公式表明调节函数
\(\phi(x)\)
的值部分由当前的重放缓冲区
\(\mathcal{B}\)
决定,但需要注意的是,
\(\mathcal{B}\)
是动态变化的,并且也受到
\(\phi(x)\)
的影响。这是因为公式中的项
\(\phi(x)\mathcal{L}_{\text{Task}}({x})\)
直接优化了生成器,该生成器合成数据样本以组成
\(\mathcal{B}\)
。从这个意义上说,考虑到
\(\mathcal{B}\)

\(\phi(x)\)
的相互影响,在训练过程中保持了类别多样性的平衡。在数据逆向过程中,平衡数据样本的类别尤为重要。

如图
2
所示,借助这两个平衡项,
SSD-KD
在样本类别和难度方面生成了温和的样本分布。

Distillation with Priority Sampling

原始的优先经验重放方法更频繁地重用重要知识的转移,从而提高学习效率。与此不同的是,论文的优先采样方法并不从环境中获得奖励,而是设计为适应无数据的知识蒸馏并从框架自身获取反馈。换句话说,优先采样方法在无数据知识蒸馏方法中扮演了相反的角色:它专注于训练一小部分高度优先的样本,而不是均匀采样,从而加速训练过程。

从当前的重放缓冲区
\(\mathcal{B}\)
中采样合成数据
\(x\)
,论文提出了一种名为优先采样(
Priority Sampling
,
PS
)的采样策略来调节采样概率,而不是均匀采样。
PS
的基本功能是衡量
\(\mathcal{B}\)
中每个样本
\(x\)
的重要性,因此引入了优先采样函数
\(\delta_{i}(x)\)

\[\begin{equation}
\delta_{i}(x) = w_{i-1}(x) KL(f_t(x;\theta_t)||f_s(x;\theta_s)),
\end{equation}
\]

\(KL\)
表示
softmax
输出
\(f_t(x;\theta_t)\)

\(f_s(x;\theta_s)\)
之间的
KL
散度;
\(\theta_t\)

\(\theta_s\)
依赖于训练步骤
\(i\)

\(w_{i}(x)\)
是用于归一化
\(\mathcal{B}\)
中样本的校准项,如下面的公式所形式化,特别地,当
\(i=0\)
时,
\(w_{-1}(x)=1\)

使用随机更新进行知识蒸馏的训练,依赖于这些更新与其期望值具有相同的分布。优先采样数据会引入偏差,因为它可能会改变数据分布,从而影响估计值所收敛的解。因此,通过引入一个重要性采样(
Importance Sampling
,
IS
)权重
\(w_i(x)\)
来校正这种偏差,用于数据样本
\(x\)

\[\begin{equation}
\label{eq_wi}
w_{i}(x)=(N \cdot P_{i}(x))^{-\beta},
\end{equation}
\]

其中
\(\beta\)
是一个超参数;
\(P_{i}(x)\)
是定义为的采样过渡概率

\[\begin{equation}
\label{pi}
P_{i}(x)=\frac{\big(\left|\delta_{i}(x)\right|+\epsilon\big)^\alpha}{\sum_{x'\in\mathcal{B}}\big(\left|\delta_{i}(x')\right|+\epsilon\big)^\alpha},
\end{equation}
\]

其中
\(\epsilon\)
是一个小的正数,用于防止在优先级为零时转移不被选择的极端情况。

优先采样函数
\(\delta(x)\)
具有两个显著特性:

  1. 随着
    delta
    值的增加,
    \(\delta(x)\)
    反映了当前
    \(\mathcal{B}\)
    中合成样本的教师模型与学生模型之间的更大信息差异。因此,学生模型应从信息差异更大的样本中进行优化,这有助于更快地获取教师模型。
  2. \(\delta(x)\)
    会随着学生模型和生成模型的每次更新迭代而动态变化。因此,当学生模型在某些样本上获得了教师模型的能力时,它会继续从相对于教师模型差异更大的样本中学习,基于新的样本分布。这进一步提升了学生模型的性能。

Experiment




如果本文对你有帮助,麻烦点个赞或在看呗~
更多内容请关注 微信公众号【晓飞的算法工程笔记】

work-life balance.

二维码如今是移动应用流量入口以及功能实现的重要工具,也是各App的流量入口,使用场景愈加丰富,广泛应用于支付、出行、餐饮、生活服务、智慧生活、零售及广告营销等主流场景。

然而,在实际生活中,扫码环境如光照强度、扫码角度、距离等,相机功能如缩放、对焦、曝光等和码图本身完整程度、弯曲程度等很大程度上会影响用户的扫码体验。

HarmonyOS SDK
统一扫码服务
(Scan Kit)作为软硬协同的系统级扫码服务,帮助开发者的应用快速构建面向各种场景的码图识别和生成能力。统一扫码服务应用了多项计算机视觉技术和AI算法技术,不仅实现了远距离自动扫码,同时还针对多种复杂扫码场景(如暗光、污损、模糊、小角度、曲面码等)做了识别优化,实现远距离码或小型码的检测和自动放大,提升扫码成功率与用户体验。

image

image

其中统一扫码服务的
默认界面扫码能力
提供系统级体验一致的扫码界面,包含相机预览流,相册扫码入口,暗光环境闪光灯开启提示,支持单码和多码识别,具备相机预授权,调用接口时,无需开发者再次申请相机权限。适用于不同扫码场景的应用开发。

能力优势

接入简单:一行代码,接入简单;系统级接口,包体0增加。

免弹窗:系统相机权限预授权,保护用户信息安全。

识别率高:应用多项CV技术,提升扫码成功率和速度。

识别距离远:应用端侧AI算法技术,实现远距离识码。

业务流程

image

开发步骤

统一扫码服务提供了默认界面扫码的能力,由扫码接口直接控制相机实现最优的相机放大控制、自适应的曝光调节、自适应对焦调节等操作,保障流畅的扫码体验,减少开发者的工作量。

以下示例为调用Scan Kit的startScanForResult接口跳转扫码页面。

1.导入默认界面扫码模块,
scanCore
提供扫码类型定义,
scanBarcode
提供拉起默认界面扫码的方法和参数,导入方法如下。

import { scanCore, scanBarcode } from '@kit.ScanKit';
// 导入默认界面需要的日志模块和错误码模块
import { hilog } from '@kit.PerformanceAnalysisKit';
import { BusinessError } from '@kit.BasicServicesKit';

2.调用startScanForResult方法拉起默认扫码界面。

通过Promise方式得到扫码结果。

[@Entry](https://my.oschina.net/u/4127701)
[@Component](https://my.oschina.net/u/3907912)
struct ScanBarCodePage {
  build() {
    Column() {
      Row() {
        Button("Promise with options")
          .backgroundColor('#0D9FFB')
          .fontSize(20)
          .fontColor('#FFFFFF')
          .fontWeight(FontWeight.Normal)
          .align(Alignment.Center)
          .type(ButtonType.Capsule)
          .width('90%')
          .height(40)
          .margin({ top: 5, bottom: 5 })
          .onClick(() => {
            // 定义扫码参数options
            let options: scanBarcode.ScanOptions = {
              scanTypes: [scanCore.ScanType.ALL],
              enableMultiMode: true,
              enableAlbum: true
            };
            // 可调用getContext接口获取当前页面关联的UIAbilityContext
            scanBarcode.startScanForResult(getContext(this), options).then((result: scanBarcode.ScanResult) => {
              // 收到扫码结果后返回
              hilog.info(0x0001, '[Scan CPSample]', `Succeeded in getting ScanResult by promise with options, result is ${JSON.stringify(result)}`);
            }).catch((error: BusinessError) => {
              hilog.error(0x0001, '[Scan CPSample]',
                `Failed to get ScanResult by promise with options. Code:${error.code}, message: ${error.message}`);
            });
          })
      }
      .height('100%')
    }
    .width('100%')
  }
}

通过Callback回调函数得到扫码结果。

@Entry
@Component
struct ScanBarCodePage {
  build() {
    Column() {
      Row() {
        Button('Callback with options')
          .backgroundColor('#0D9FFB')
          .fontSize(20)
          .fontColor('#FFFFFF')
          .fontWeight(FontWeight.Normal)
          .align(Alignment.Center)
          .type(ButtonType.Capsule)
          .width('90%')
          .height(40)
          .margin({ top: 5, bottom: 5 })
          .onClick(() => {
            // 定义扫码参数options
            let options: scanBarcode.ScanOptions = {
              scanTypes: [scanCore.ScanType.ALL],
              enableMultiMode: true,
              enableAlbum: true
            };
            // 可调用getContext接口获取当前页面关联的UIAbilityContext
            scanBarcode.startScanForResult(getContext(this), options,
              (error: BusinessError, result: scanBarcode.ScanResult) => {
                if (error) {
                  hilog.error(0x0001, '[Scan CPSample]',
                    `Failed to get ScanResult by callback with options. Code: ${error.code}, message: ${error.message}`);
                  return;
                }
                // 收到扫码结果后返回
                hilog.info(0x0001, '[Scan CPSample]', `Succeeded in getting ScanResult by callback with options, result is ${JSON.stringify(result)}`);
              })
          })
      }
      .height('100%')
    }
    .width('100%')
  }
}

了解更多详情>>

访问
统一扫码服务联盟官网

获取
默认界面扫码服务开发指导文档

在现代桌面应用开发中,使用 Electron 加载远程服务器托管的前端资源,再与本地 API 交互,能够带来灵活的部署和强大的本地功能支持。这种方式不仅提升了开发效率,还能充分利用 PC 端的资源和性能。

本文将深入解析如何使用 Electron 实现这一架构,并探讨其背后的关键技术,包括
ipcMain

ipcRenderer
进程间通讯,以及
preload.js
安全交互等内容。你将学会如何打造既能随时更新前端,又能高效利用本地硬件资源的桌面应用。

1. 服务器资源与 Electron 的高效结合

通常,我们的前端资源(HTML、CSS、JavaScript)可以托管在远程服务器上,比如通过 Nginx、Apache 等托管工具来部署静态页面和资源。

Electron 使用
BrowserWindow
加载这些远程资源:

const { app, BrowserWindow } = require('electron');const path = require('path');

function createWindow() {
const win = newBrowserWindow({
width:
800,
height:
600,
webPreferences: {
preload: path.join(__dirname,
'preload.js'),
nodeIntegration:
false,
contextIsolation:
true,
},
});
//加载服务器托管的前端页面 win.loadURL('https://example.com');
}

app.whenReady().then(createWindow);

这样,Electron 应用可以直接从服务器加载最新的前端资源,同时主进程负责处理本地 API 的调用和交互。

2.
preload.js
:前端与本地 API 的安全桥梁

Electron 提供了
preload.js
,这是一个在 Web 页面加载之前运行的脚本,它允许安全地在前端和主进程之间创建通信通道。通过
preload.js
,我们可以将本地 API 的访问封装起来,并通过
contextBridge
暴露给前端。

const { contextBridge, ipcRenderer } = require('electron');

contextBridge.exposeInMainWorld(
'electronAPI', {
sendMessage: (channel, data)
=>{const validChannels = ['toMain'];if(validChannels.includes(channel)) {
ipcRenderer.send(channel, data);
}
},
receiveMessage: (channel, func)
=>{const validChannels = ['fromMain'];if(validChannels.includes(channel)) {
ipcRenderer.on(channel, (
event, ...args) =>func(...args));
}
}
});

这种方式确保前端无法直接访问 Node.js API,从而提高了应用的安全性。

3. 利用
ipcMain

ipcRenderer
实现前后端通讯

前端通过
preload.js
与主进程进行消息交互,而主进程通过
ipcMain
监听来自前端的请求。以下是主进程中如何处理前端请求并与本地 API 交互的示例:

const { ipcMain } = require('electron');

ipcMain.on(
'toMain', (event, data) =>{
console.log(
'收到前端数据:', data);//调用本地 API 或进行其他操作 const response =callLocalAPI(data);//发送结果给前端 event.sender.send('fromMain', response);
});

function callLocalAPI(data) {
return`处理后的数据: ${data}`;
}

前端可以使用暴露的 API 来发送消息并接收响应:

<script>window.electronAPI.sendMessage('toMain', '这是来自前端的数据');

window.electronAPI.receiveMessage(
'fromMain', (response) =>{
console.log(
'收到主进程响应:', response);
});
</script>

4. 综合工作流

通过这套架构,Electron 可以:

  1. 从服务器加载和渲染最新的前端资源。
  2. 使用
    preload.js
    提供安全的接口,允许前端与本地 API 进行通讯。
  3. 利用
    ipcMain

    ipcRenderer
    实现前后端的双向通讯。

结语

这种 Electron 与服务器资源结合的架构,不仅让前端资源管理更加灵活,还能高效利用本地 API 和硬件资源。无论是需要频繁更新的前端界面,还是依赖本地系统功能的应用场景,这种方式都能提供强大支持。

通过本文的示例,你已经掌握了如何通过 Electron 加载服务器资源并与本地 API 交互的核心技术,为你的桌面应用注入更多可能性。

让我们一起动手,打造更加灵活与强大的桌面应用吧!

导读:大模型能力的发展和成熟,催生出新一代智能化 BI—— ChatBI,即通过自然语言处理(NLP)与大型语言模型(LLMs)的结合,极大简化数据分析过程,提高效率并降低分析门槛。火山引擎数智平台旗下智能数据洞察产品 DataWind 近期上线 ChatBI 能力,提供智能修复、多语法适用等能力,在性能上实现秒级响应、一键生成。用户只需要通过文字描述需求, 就能生成指标,快速实现数据获取、分析计算与图表搭建,大幅降低数据消费门槛。本篇文章将从技术架构、实现路径、总结展望几个方面,拆解火山引擎数智平台如何落地 ChatBI 能力。

BI 其实是一个由来已久的名词。其中 I——“intelligence”的内涵已经随着时间推移和时代发展而逐渐发生变化。

起初,人们认为在数据仪表盘和看板上能够进行筛选条件变更与维度下钻就是智能化表现。

而随着平台更新迭代,更多高阶、复杂的功能以更易操作的形式更新到平台中,让没有计算机背景或编程背景的人也能够深切体会到代码、计算机或者大数据时代所带来的智能之感。

随着 AI 时代的来临,大家对于智能化有了更多期待。例如: 它是否能够“猜到”自己的想法进行智能推荐?或是,当看到数据异常,它能否帮忙找出原因?

客观而言,从 2018 年开始开发的抖音集团内部 BI 平台起步较晚。 因此其直接跳过了 BI 平台早期发展阶段,从立项之初,它的目标便是成为能够满足公司内部几乎所有数据分析需求的数据分析平台。

在抖音集团内部,BI 平台建设分为以下几个阶段:

一是 2020 年前后的开发建设。在这个阶段投入了大量资源,对结果归因相关功能进行开发,希望能够帮助用户解决归因问题。

二是在 2021 年 4 月,发布了低代码的可视化建模工具。原因在于,团队不想让用户在数据分析的过程中发现数据尚未准备完毕时,需要去专门联系数仓开发人员重新准备一份数据。为此开发了可视化建模工具,希望用户仅需要进行简单的拖拉拽操作就可以轻松处理数据。

三是 2023 年年底。内部团队面对迅速发展的 ChatGPT,认为它会对 BI 产生具有如“掀桌子”一般颠覆性的影响,因此经过一段时间的尝试,便在今年 4 月份对内进行了产品发布。就目前而言落地效果不错,已经有几千人在高频使用这一内部产品。

当前,火山引擎数智平台旗下的智能数据洞察 DataWind 已构建起包含了数据准备与管理,数据分析,以及多端展示等功能的相对完善的产品能力矩阵,同时赋予产品系统高度可运维优势。

截至目前,抖音集团 80%内部员工成为产品月活用户,同时在工作日单日的产品最低查询量基本处于 200w 次以上。

火山引擎数智平台高性能数据分析架构方案

数据驱动决策,是在抖音集团内部深入人心的重要概念,并与公司所推行的 OKR 理念相互契合。由于 OKR 通常以指标化方式去衡量,在指标出现问题需要进行排查探寻原因时,数据分析便成了必不可少的过程。同时,在排查过程中用户脑海中会同时存在多种分析思路,如果数据分析时间过长,就会将原本的分析思路打断。因此,为了实现高速分析,企业内部员工用户对分析平台的性能有着极大要求。

尽管性能十分重要,但 BI 平台开发厂商往往认为其更多与引擎有关:引擎能力较差会导致 BI 所能处理的事情并不多。

但数据集、数据源、数据量的大小以及 Query 的复杂程度等并不是用户所关心的,他们关心的是自己的数据分析能否能快速完成。因此在提高性能方面的开发面临着很大挑战。

为了满足用户对性能的需求,开发中采取了有别于主流 BI 厂商的思路。虽然 DataWind 产品支持直连目前通用的大部分数据源、数据引擎与数据库。但在企业内部用户更多地使用“抽取”,即围绕自研分析性数据库 ByteHouse 建设了非常重的抽取链路,把公司内几乎全部需要进行数据分析的数据全部放入 ByteHouse 中。

由于数据存放方式对于查询效率有着极大影响,因此 BI 团队使用了大量的 ByteHouse 集群来满足用户对于实时连接、离线连接、不同表引擎连接的需求

同时,如何充分有效地利用 ByteHouse 高性能引擎也十分重要。比如应该向什么样的集群推荐数据集,选择什么样的表引擎,以及确定什么样的分片和排序键策略等。这些为问题对于性能而言都相当关键。

在这里,先简单介绍一下 ByteHouse。相较于原生 Clickhouse,ByteHouse 针对多个领域做了性能优化。

首先,是 HaMergeTree 方面的优化工作。HaMergeTree 对于大部分企业用户而言都不可或缺。原生 ClickHouse 在对 Apache ZooKeeper(ZK)存在较大依赖,在文件的 part 信息处理方面依赖性更甚。这就导致 ClickHouse 处理大规模数据集时,易造成 ZK 资源紧张,管理的 znode 数量暴增,影响系统性能和稳定性。

ByteHouse 对此进行了大量优化,从而降低了对 ZK 的依赖程度。目前在 ByteHouse 中,对 ZK 的一脸仅仅存在于 schema 信息,以及生成自增序列等极少数场景中,从而保证了 ByteHouse 的整体性能和可用性。

在 HAUniqueMergeTree,即原生 ClickHouse 的 Raplacing MergeTree 方面。相对而言,ClickHouse 引擎在读取方面并不高效,而 ByteHouse 在处理此方面问题时,会通过建立一定的索引实现对记录的快速更新和标记删除,从而提高性能。

此外,原生 Clickhouse 的 join 能力因为对 coordinator 节点压力较大的问题被大家诟病已久。ByteHouse 在这个方面实现了真正的分布式 join,同时也基于此做了大量优化器方面的工作。例如当大表 join 小表时,ByteHouse 会根据小表的数据情况进行自主判断,去对大表中的部分数据免读或免下封。

总体而言,把大量数据导入 ByteHouse 并不意味着表的数量很多。在抖音集团内部,大家更愿意把更全面、更明细的数据导入到 ByteHouse 集群中,从而避免在做数据分析的过程中出现某一方面的数据不存在或者明细级别不够的情况。

在使用到非常细的粒度的场景,团队认为大部分查询是基于某些高频指标维度,去找到非常明细的数据所做的查询。因此会很容易地想到解决方案,即建立一些 Cube 或物化视图,并且建立一些自动路由。

对于用户而言,这些操作都是十分透明的。而从工程上值得一提的是,团队并没有使用 ByteHouse 自带的物化视图或是 projection 方式,因为在开发实践测试中,发现这种方式对于集群与整体性能有负面影响。目前,开发中主要使用基于 Hadoop 链路与基于 Spark 的链路去进行 Cube 建设,并且由此实现自动路由。从用户角度来看,用户会面对一张宽泛且明细力度极细的大表。

但这种方法的副作用在于由于产品的各个业务线都采取了付费使用形式,为数据集建设了大量聚合表必然会导致成本的上升。这就涉及到一个新的问题:在满足了用户的速度要求情况下,如何降低成本?

针对这个问题,解决的思路是提供数据冷热分层,对于最为常用的数据,例如最近 7 天或 14 天的数据,可以放置于 ByteHouse 中进行存储。而相对距离当前时间较远的数据,则放置在存算分离的 ByteHouse 集群当中,通过更为廉价的方式来实现查询。至于时间更为久远的数据,比如过去一年的数据,就存于 Hive 表内,可以通过 Python 或者 Spark 的方式来进行查询。

而之所以保留对以 Sparck 方式进行兜底查询这一方式的支持,是由于 MPP 相关数据库在兜底能力方面普遍存在硬伤。故而采用 Spark 方式来兜底,如此一来,至少能够确保用户在极为极端的情况下仍然能查询到数据结果。

除此之外,性能优化与成本治理也是值得研究的问题。对此开发中所采用的是一种较为偏向“人民战争”的方式。鉴于仅依赖平台运维团队来监控所有性能指标及具体数据库表,难以满足集团公司庞大的业务需求,团队选择将这种监控与优化的能力内嵌于产品体系中。

从而让每个业务线的负责人,乃至每个项目管理者,都能直观地了解到哪些数据集消耗资源较多、哪些数据集的成本效益比较低——即投入大额资金但查询频率并不高的情况。该策略还可协助他们识别出哪些部分可通过构建多级聚合来提升性能,以及在确保性能不受影响的前提下,如何实施成本控制措施以实现更高效的资源配置。

通过这种方式,不仅能够分散管理和优化的压力,还能促进全员对资源效率的关注与参与,确保了整个集团在规模扩张的同时保持成本效益与服务性能的最优化。

BI + AI 实现智能数据洞察

抖音集团内部在 BI 平台建设阶段,对智能部分投入较多。

而对智能部分的分享可以大致分为三个部分。

其一为数据开发,旨在帮助进行数据准备的人员能够准备更具价值的数据;其二是数据分析,期望能够助力用户进行异常指标查询以及异常归因;其三为数据消费,通过对话式问答的方式来提升提取信息的效率。

数据开发的场景相对简单,团队的工作主要是集成多种 AI 算子至低代码可视化建模工具中,其中运用较多的是预测能力。而使用预测的场景也非常容易理解。

假设用户有一张表,其中某一列可能表示几天后的数据,若此时用户已知道其他列的信息和历史数据,便会希望通过机器学习的方式预测出该列的新值。从目前来看,此类需求较多。从算子角度来讲,产品母线已集成约 40 多种算子,其中特征工程算子与预测算子是被频繁使用的两类。

接着是数据分析场景。在数据分析场景中,开发团队希望能够帮助用户更快捷地进行异常指标查询以及异常归因,不想再为用户配置例如当数据指标低于 10%或 5%再发送警告此类傻瓜式警告方式。

团队想要开发一个更为灵活、能够反映指标季节性的预警系统,因此在开发中采用了 STO 算法,同时结合指标平滑技术,利用残差结合历史数据计算出指标的波动范围,当超出这一波动范围时,就会进行告警。

从产品形态方面分析,归因可以分为以下几类。

从产品形态上即时归因较易理解,即用户在发现异常时只需点击一下,系统便会进行归因。就维度选择而言,开发团队参照了一种基于基尼系数的维度选择方式,基尼系数常被联合国用于贫富差异比较,将其理解成维度后,可把每个维度视为一个国家,若某个维度中维值对某一指标的贡献较为平均且无明显差异性,则认为该维度可能并非主要原因。

在确定维度后会通过一系列方法计算维值的贡献率。即时归因对即时性要求较高,其可以在短时间,比如 15 秒钟左右,返回查询结果,但即时归因能分析的事情相对较少,它不会进行相关指标分析,仅会做维度分析,也不会做过多维度组合相关分析,总体功能较为简单。

而另一种归因——洞察报告的功能则相对丰富,洞察报告通过异步通知模式可以处理相对复杂的需求,能够分析不同指标和进行多种维度组合。

用户归因前进行配置,比如选择指标归因或维度归因,选择大致组合后便可生成洞察报告,洞察报告既可以在系统上查看,也可以推送给相应的 IM。

此外,还有一种在内部使用较多的归因——指标分析树。

在集团内部,大家在进行 OKR 对齐时,指标往往会形成类似指标体系的东西,即:上级重视 GMV 等指标,而下级更关注 PV 等指标,这种差异便会形成树状指标结构。如果对于指标存在疑问,就会进行固化的基于维度的分析,其总体思路是将指标分析过程进行固化,确保在查看 OKR 或指标时,能够清楚知道出现异常的板块、维度和节点。

归因功能从实现角度而言整体相对简单,难点主要在于产品设计与算法相关处理,而其在工程角度也是较为简单的问题。此外,异步的洞察报告和指标分析数调度在实现时,要尽量避免对在线查询产生影响,要尽量减少占用在线查询资源。

在积极开展指标归因相关工作时,大模型出现了。随后,团队投入了大量的时间去探索与大模型相关的能力,同时也耗费了较多的资源。从结果方面来看,目前集团内部已有大几千人成为 ABI 的 Copilot 常用用户,因此整体而言取得了不错的成果。

从探索角度而言,团队开发了多样化的场景,但落地结果有部分相对成功,也有部分相对失败。

如今回顾那些不太成功的场景,都存在一个共性,即所生成内容的质量并非很高。也就是说,相对而言或许产品交互方面它们还有很大的改进空间,但在内容质量方面的调优往往较为困难。

例如,用户期望大模型能帮助进行归因,告知数据为何不对或者接下来应朝哪个方向去查看。

关于这方面的能力,团队最初将其上线的原因在于其表现实际上超出了预期。从开发者角度来看,特别容易以较低的预期来看待大模型相关事宜,觉得它能做到与自己一样的事情就感觉它似乎表现得已经不错了,但实际上从用户解决问题的角度来看,往往生成的内容质量没有那么高。所以在这一点上特别容易让团队产生较为乐观的预期,而这往往会导致落地效果或落地姿态不够理想。

目前取得成功的案例存在一些共性特点。首先,如果功能是为了解决诸如用户在解决问题过程中本身就需要去搜索资料的问题,与 ChatGPT 相结合往往能获得较好的解决方式。

在代码开发场景方面,团队产品功能内部落地情况较好,包括此前列举的 SQL 查询,围绕 ABI 所支持的高阶 Notebook 做法。这些功能能够取代用户在网上搜索查看大量 Stack overflow 帖子,之后提炼代码编辑思路的场景,而在大模型加持的代码编辑器中,DataWind 提供了如解释 SQL、优化 SQL、生成注释以及报错修复等一系列功能。

又或者说数据准备的第一步:源数据的录入。起初在录入指标时,往往需要进行诸多翻译工作为指标命名。

若是处于多语言环境,还需配置该指标的外语名称。对于这类问题,以往解决时通常需要查阅公开资料,查询相关单词的英文写法等。而这部分的工作,大模型能够有较为出色的表现,能够极大地节省用户精力。

而在仪表盘的探索分析及解读中,大模型能发挥的最大作用在于帮助用户展开润色工作,因为用户可能需要迅速地将数据结果发送给上级,而自己书写解读内容可能会相对困难。在这一场景中,DataWind 除了对某一图表进行数据解释外,还会推荐一些 follow up 问题,而这些 follow up 问题实则完全由 GPT 所推荐。

当用户点击一个问题时,它亦会描述接下来要进行的操作,这也变相回应了另一个问题,即如何让用户知晓模型后续的行为。因为目前至关重要的一点是,以大模型在现阶段的能力,其回答是无法做到百分百准确的。

而在严肃场合,需要的是一个非常精准的数字,用户会非常想要了解其统计口径是什么,它又是如何得出这个结论的。因此在落地场景中,一个极为重要的原则是必须让用户清楚大模型到底做了什么,或者说大模型做了哪些部分,以及接下来将如何处理这个请求,这个数据究竟是如何得来的,这一点十分关键,否则,即便模型准确率能够达到 95%以上,其在数据产品中的落地也较为困难。

接着再来看一下 SQL 查询的场景,本产品能够进行解释以及优化。在 editor 中可以运用自然语言来帮助生成相应的 SQL 以及与 notebook 相关的一些代码。

从实现角度而言。第一点便是合规与内容审核问题。抖音集团内部实践最初采用了 GPT 模型,进行了多种尝试,包括 GPT 3 的 tuning 等,对比后选择了 GPT 4,同时也在努力对接公司自研大模型,因此在内容审核方面较为吃力,例如:若认为 table 的 schema 不那么敏感,而 table 的数据敏感,那么那些维值应如何处理?是否需要进行向量化匹配?这会涉及到一系列的技术和工程问题。

再如精调问题,究竟是否要采用模型精调以及何时采用?即便到目前内部也未完全放弃模型精调这一路线,开发团队对于精调一事的理解更多是以空间换取时间,因为有时团队会发现当用户将私域问题描述得极为详尽时提示语过长,过长的提示语一方面可能无法输入,另一方面也可能影响整体使用效率。此时要通过部分精调的方式来减少需要提供的 prompt 数量。

总结与展望

简单总结几个未来展望的要点:

一,企业级 BI 正逐渐成为新趋势,曾经的普遍情况是诸多业务部门各自购买 BI,而全公司或全员使用的 BI 在当时并不重要,但如今其重要性愈发凸显。

二,指标治理以及 AI 能力也是至关重要的部分。

三,团队认为数据消费能够推动数据建设,总体建设思路是将上层的数据消费打造得极为繁荣,在相对繁荣之后,会持续向下层的数据建设,如 ETL 部分、数仓部分以及数据湖部分提出新的诉求,从而带动下层基础设施的建设。

点击
火山引擎DataWind
了解更多