【深度学习系列】卷积神经网络CNN原理详解(一)——基本原理(1)

上篇文章我们给出了用paddlepaddle来做手写数字识别的示例,并对网络结构进行到了调整,提高了识别的精度。有的同学表示不是很理解原理,为什么传统的机器学习算法,简单的神经网络(如多层感知机)都可以识别手写数字,我们要采用卷积神经网络CNN来进行别呢?CNN到底是怎么识别的?用CNN有哪些优势呢?我们下面就来简单分析一下。在讲CNN之前,为避免完全零基础的人看不懂后面的讲解,我们先简单回顾一下传统的神经网络的基本知识。

神经网络的预备知识

为什么要用神经网络?

  • 特征提取的高效性。

大家可能会疑惑,对于同一个分类任务,我们可以用机器学习的算法来做,为什么要用神经网络呢?大家回顾一下,一个分类任务,我们在用机器学习算法来做时,首先要明确feature和label,然后把这个数据"灌"到算法里去训练,最后保存模型,再来预测分类的准确性。但是这就有个问题,即我们需要实现确定好特征,每一个特征即为一个维度,特征数目过少,我们可能无法精确的分类出来,即我们所说的欠拟合,如果特征数目过多,可能会导致我们在分类过程中过于注重某个特征导致分类错误,即过拟合。

举个简单的例子,现在有一堆数据集,让我们分类出西瓜和冬瓜,如果只有两个特征:形状和颜色,可能没法分区来;如果特征的维度有:形状、颜色、瓜瓤颜色、瓜皮的花纹等等,可能很容易分类出来;如果我们的特征是:形状、颜色、瓜瓤颜色、瓜皮花纹、瓜蒂、瓜籽的数量,瓜籽的颜色、瓜籽的大小、瓜籽的分布情况、瓜籽的XXX等等,很有可能会过拟合,譬如有的冬瓜的瓜籽数量和西瓜的类似,模型训练后这类特征的权重较高,就很容易分错。这就导致我们在特征工程上需要花很多时间和精力,才能使模型训练得到一个好的效果。然而神经网络的出现使我们不需要做大量的特征工程,譬如提前设计好特征的内容或者说特征的数量等等,我们可以直接把数据灌进去,让它自己训练,自我“修正”,即可得到一个较好的效果。

  • 数据格式的简易性

在一个传统的机器学习分类问题中,我们“灌”进去的数据是不能直接灌进去的,需要对数据进行一些处理,譬如量纲的归一化,格式的转化等等,不过在神经网络里我们不需要额外的对数据做过多的处理,具体原因可以看后面的详细推导。

  • 参数数目的少量性

在面对一个分类问题时,如果用SVM来做,我们需要调整的参数需要调整核函数,惩罚因子,松弛变量等等,不同的参数组合对于模型的效果也不一样,想要迅速而又准确的调到最适合模型的参数需要对背后理论知识的深入了解(当然,如果你说全部都试一遍也是可以的,但是花的时间可能会更多),对于一个基本的三层神经网络来说(输入-隐含-输出),我们只需要初始化时给每一个神经元上随机的赋予一个权重w和偏置项b,在训练过程中,这两个参数会不断的修正,调整到最优质,使模型的误差最小。所以从这个角度来看,我们对于调参的背后理论知识并不需要过于精通(只不过做多了之后可能会有一些经验,在初始值时赋予的值更科学,收敛的更快罢了)

有哪些应用?

应用非常广,不过大家注意一点,我们现在所说的神经网络,并不能称之为深度学习,神经网络很早就出现了,只不过现在因为不断的加深了网络层,复杂化了网络结构,才成为深度学习,并在图像识别、图像检测、语音识别等等方面取得了不错的效果。

基本网络结构

一个神经网络最简单的结构包括输入层、隐含层和输出层,每一层网络有多个神经元,上一层的神经元通过激活函数映射到下一层神经元,每个神经元之间有相对应的权值,输出即为我们的分类类别。

详细数学推导

去年中旬我参考吴恩达的UFLDL和mattmazur的博客写了篇文章详细讲解了一个最简单的神经网络从前向传播到反向传播的直观推导,大家可以先看看这篇文章--一文弄懂神经网络中的反向传播法--BackPropagation。

优缺点

前面说了很多优点,这里就不多说了,简单说说缺点吧。我们试想一下如果加深我们的网络层,每一个网络层增加神经元的数量,那么参数的个数将是M*N(m为网络层数,N为每层神经元个数),所需的参数会非常多,参数一多,模型就复杂了,越是复杂的模型就越不好调参,也越容易过拟合。此外我们从神经网络的反向传播的过程来看,梯度在反向传播时,不断的迭代会导致梯度越来越小,即梯度消失的情况,梯度一旦趋于0,那么权值就无法更新,这个神经元相当于是不起作用了,也就很难导致收敛。尤其是在图像领域,用最基本的神经网络,是不太合适的。后面我们会详细讲讲为啥不合适。

为什么要用卷积神经网络?

传统神经网络的劣势

前面说到在图像领域,用传统的神经网络并不合适。我们知道,图像是由一个个像素点构成,每个像素点有三个通道,分别代表RGB颜色,那么,如果一个图像的尺寸是(28,28,1),即代表这个图像的是一个长宽均为28,channel为1的图像(channel也叫depth,此处1代表灰色的图像)。如果使用全连接的网络结构,即,网络中的神经与与相邻层上的每个神经元均连接,那就意味着我们的网络有28 * 28 =784个神经元,hidden层采用了15个神经元,那么简单计算一下,我们需要的参数个数(w和b)就有:784*15*10+15+10=117625个,这个参数太多了,随便进行一次反向传播计算量都是巨大的,从计算资源和调参的角度都不建议用传统的神经网络。(评论中有同学对这个参数计算不太理解,我简单说一下:图片是由像素点组成的,用矩阵表示的,28*28的矩阵,肯定是没法直接放到神经元里的,我们得把它“拍平”,变成一个28*28=784 的一列向量,这一列向量和隐含层的15个神经元连接,就有784*15=11760个权重w,隐含层和最后的输出层的10个神经元连接,就有11760*10=117600个权重w,再加上隐含层的偏置项15个和输出层的偏置项10个,就是:117625个参数了)

卷积神经网络是什么?

三个基本层

  •  卷积层(Convolutional Layer)

上文提到我们用传统的三层神经网络需要大量的参数,原因在于每个神经元都和相邻层的神经元相连接,但是思考一下,这种连接方式是必须的吗?全连接层的方式对于图像数据来说似乎显得不这么友好,因为图像本身具有“二维空间特征”,通俗点说就是局部特性。譬如我们看一张猫的图片,可能看到猫的眼镜或者嘴巴就知道这是张猫片,而不需要说每个部分都看完了才知道,啊,原来这个是猫啊。所以如果我们可以用某种方式对一张图片的某个典型特征识别,那么这张图片的类别也就知道了。这个时候就产生了卷积的概念。举个例子,现在有一个4*4的图像,我们设计两个卷积核,看看运用卷积核后图片会变成什么样。

由上图可以看到,原始图片是一张灰度图片,每个位置表示的是像素值,0表示白色,1表示黑色,(0,1)区间的数值表示灰色。对于这个4*4的图像,我们采用两个2*2的卷积核来计算。设定步长为1,即每次以2*2的固定窗口往右滑动一个单位。以第一个卷积核filter1为例,计算过程如下:

1 feature_map1(1,1) = 1*1 + 0*(-1) + 1*1 + 1*(-1) = 1 
2 feature_map1(1,2) = 0*1 + 1*(-1) + 1*1 + 1*(-1) = -1 
3 ``` 
4 feature_map1(3,3) = 1*1 + 0*(-1) + 1*1 + 0*(-1) = 2

可以看到这就是最简单的内积公式。feature_map1(1,1)表示在通过第一个卷积核计算完后得到的feature_map的第一行第一列的值,随着卷积核的窗口不断的滑动,我们可以计算出一个3*3的feature_map1;同理可以计算通过第二个卷积核进行卷积运算后的feature_map2,那么这一层卷积操作就完成了。feature_map尺寸计算公式:[ (原图片尺寸 -卷积核尺寸)/ 步长 ] + 1。这一层我们设定了两个2*2的卷积核,在paddlepaddle里是这样定义的:

conv_pool_1 = paddle.networks.simple_img_conv_pool(
        input=img,
        filter_size=3,
        num_filters=2,
        num_channel=1,
        pool_stride=1,
        act=paddle.activation.Relu())

这里调用了networks里simple_img_conv_pool函数,激活函数是Relu(修正线性单元),我们来看一看源码里外层接口是如何定义的:

1 def simple_img_conv_pool(input,
 2                          filter_size,
 3                          num_filters,
 4                          pool_size,
 5                          name=None,
 6                          pool_type=None,
 7                          act=None,
 8                          groups=1,
 9                          conv_stride=1,
10                          conv_padding=0,
11                          bias_attr=None,
12                          num_channel=None,
13                          param_attr=None,
14                          shared_bias=True,
15                          conv_layer_attr=None,
16                          pool_stride=1,
17                          pool_padding=0,
18                          pool_layer_attr=None):
19     """
20     Simple image convolution and pooling group.
21     Img input => Conv => Pooling => Output.
22     :param name: group name.
23     :type name: basestring
24     :param input: input layer.
25     :type input: LayerOutput
26     :param filter_size: see img_conv_layer for details.
27     :type filter_size: int
28     :param num_filters: see img_conv_layer for details.
29     :type num_filters: int
30     :param pool_size: see img_pool_layer for details.
31     :type pool_size: int
32     :param pool_type: see img_pool_layer for details.
33     :type pool_type: BasePoolingType
34     :param act: see img_conv_layer for details.
35     :type act: BaseActivation
36     :param groups: see img_conv_layer for details.
37     :type groups: int
38     :param conv_stride: see img_conv_layer for details.
39     :type conv_stride: int
40     :param conv_padding: see img_conv_layer for details.
41     :type conv_padding: int
42     :param bias_attr: see img_conv_layer for details.
43     :type bias_attr: ParameterAttribute
44     :param num_channel: see img_conv_layer for details.
45     :type num_channel: int
46     :param param_attr: see img_conv_layer for details.
47     :type param_attr: ParameterAttribute
48     :param shared_bias: see img_conv_layer for details.
49     :type shared_bias: bool
50     :param conv_layer_attr: see img_conv_layer for details.
51     :type conv_layer_attr: ExtraLayerAttribute
52     :param pool_stride: see img_pool_layer for details.
53     :type pool_stride: int
54     :param pool_padding: see img_pool_layer for details.
55     :type pool_padding: int
56     :param pool_layer_attr: see img_pool_layer for details.
57     :type pool_layer_attr: ExtraLayerAttribute
58     :return: layer's output
59     :rtype: LayerOutput
60     """
61     _conv_ = img_conv_layer(
62         name="%s_conv" % name,
63         input=input,
64         filter_size=filter_size,
65         num_filters=num_filters,
66         num_channels=num_channel,
67         act=act,
68         groups=groups,
69         stride=conv_stride,
70         padding=conv_padding,
71         bias_attr=bias_attr,
72         param_attr=param_attr,
73         shared_biases=shared_bias,
74         layer_attr=conv_layer_attr)
75     return img_pool_layer(
76         name="%s_pool" % name,
77         input=_conv_,
78         pool_size=pool_size,
79         pool_type=pool_type,
80         stride=pool_stride,
81         padding=pool_padding,
82         layer_attr=pool_layer_attr)

我们在Paddle/python/paddle/v2/framework/nets.py 里可以看到simple_img_conv_pool这个函数的定义:

 1 def simple_img_conv_pool(input,
 2                          num_filters,
 3                          filter_size,
 4                          pool_size,
 5                          pool_stride,
 6                          act,
 7                          pool_type='max',
 8                          main_program=None,
 9                          startup_program=None):
10     conv_out = layers.conv2d(
11         input=input,
12         num_filters=num_filters,
13         filter_size=filter_size,
14         act=act,
15         main_program=main_program,
16         startup_program=startup_program)
17 
18     pool_out = layers.pool2d(
19         input=conv_out,
20         pool_size=pool_size,
21         pool_type=pool_type,
22         pool_stride=pool_stride,
23         main_program=main_program,
24         startup_program=startup_program)
25     return pool_out

可以看到这里面有两个输出,conv_out是卷积输出值,pool_out是池化输出值,最后只返回池化输出的值。conv_out和pool_out分别又调用了layers.py的conv2d和pool2d,去layers.py里我们可以看到conv2d和pool2d是如何实现的:

conv2d:

def conv2d(input,
       num_filters,
       name=None,
       filter_size=[1, 1],
       act=None,
       groups=None,
       stride=[1, 1],
       padding=None,
       bias_attr=None,
       param_attr=None,
       main_program=None,
       startup_program=None):
helper = LayerHelper('conv2d', **locals())
dtype = helper.input_dtype()

num_channels = input.shape[1]
if groups is None:
    num_filter_channels = num_channels
else:
    if num_channels % groups is not 0:
        raise ValueError("num_channels must be divisible by groups.")
    num_filter_channels = num_channels / groups

if isinstance(filter_size, int):
    filter_size = [filter_size, filter_size]
if isinstance(stride, int):
    stride = [stride, stride]
if isinstance(padding, int):
    padding = [padding, padding]

input_shape = input.shape
filter_shape = [num_filters, num_filter_channels] + filter_size

std = (2.0 / (filter_size[0]**2 * num_channels))**0.5
filter = helper.create_parameter(
    attr=helper.param_attr,
    shape=filter_shape,
    dtype=dtype,
    initializer=NormalInitializer(0.0, std, 0))
pre_bias = helper.create_tmp_variable(dtype)

helper.append_op(
    type='conv2d',
    inputs={
        'Input': input,
        'Filter': filter,
    },
    outputs={"Output": pre_bias},
    attrs={'strides': stride,
           'paddings': padding,
           'groups': groups})

pre_act = helper.append_bias_op(pre_bias, 1)

return helper.append_activation(pre_act)

pool2d:

 def pool2d(input,
 2            pool_size,
 3            pool_type,
 4            pool_stride=[1, 1],
 5            pool_padding=[0, 0],
 6            global_pooling=False,
 7            main_program=None,
 8            startup_program=None):
 9     if pool_type not in ["max", "avg"]:
10         raise ValueError(
11             "Unknown pool_type: '%s'. It can only be 'max' or 'avg'.",
12             str(pool_type))
13     if isinstance(pool_size, int):
14         pool_size = [pool_size, pool_size]
15     if isinstance(pool_stride, int):
16         pool_stride = [pool_stride, pool_stride]
17     if isinstance(pool_padding, int):
18         pool_padding = [pool_padding, pool_padding]
19 
20     helper = LayerHelper('pool2d', **locals())
21     dtype = helper.input_dtype()
22     pool_out = helper.create_tmp_variable(dtype)
23 
24     helper.append_op(
25         type="pool2d",
26         inputs={"X": input},
27         outputs={"Out": pool_out},
28         attrs={
29             "poolingType": pool_type,
30             "ksize": pool_size,
31             "globalPooling": global_pooling,
32             "strides": pool_stride,
33             "paddings": pool_padding
34         })
35

大家可以看到,具体的实现方式还调用了layers_helper.py:

  1 import copy
  2 import itertools
  3 
  4 from paddle.v2.framework.framework import Variable, g_main_program, \
  5     g_startup_program, unique_name, Program
  6 from paddle.v2.framework.initializer import ConstantInitializer, \
  7     UniformInitializer
  8 
  9 
 10 class LayerHelper(object):
 11     def __init__(self, layer_type, **kwargs):
 12         self.kwargs = kwargs
 13         self.layer_type = layer_type
 14         name = self.kwargs.get('name', None)
 15         if name is None:
 16             self.kwargs['name'] = unique_name(self.layer_type)
 17 
 18     @property
 19     def name(self):
 20         return self.kwargs['name']
 21 
 22     @property
 23     def main_program(self):
 24         prog = self.kwargs.get('main_program', None)
 25         if prog is None:
 26             return g_main_program
 27         else:
 28             return prog
 29 
 30     @property
 31     def startup_program(self):
 32         prog = self.kwargs.get('startup_program', None)
 33         if prog is None:
 34             return g_startup_program
 35         else:
 36             return prog
 37 
 38     def append_op(self, *args, **kwargs):
 39         return self.main_program.current_block().append_op(*args, **kwargs)
 40 
 41     def multiple_input(self, input_param_name='input'):
 42         inputs = self.kwargs.get(input_param_name, [])
 43         type_error = TypeError(
 44             "Input of {0} layer should be Variable or sequence of Variable".
 45             format(self.layer_type))
 46         if isinstance(inputs, Variable):
 47             inputs = [inputs]
 48         elif not isinstance(inputs, list) and not isinstance(inputs, tuple):
 49             raise type_error
 50         else:
 51             for each in inputs:
 52                 if not isinstance(each, Variable):
 53                     raise type_error
 54         return inputs
 55 
 56     def input(self, input_param_name='input'):
 57         inputs = self.multiple_input(input_param_name)
 58         if len(inputs) != 1:
 59             raise "{0} layer only takes one input".format(self.layer_type)
 60         return inputs[0]
 61 
 62     @property
 63     def param_attr(self):
 64         default = {'name': None, 'initializer': UniformInitializer()}
 65         actual = self.kwargs.get('param_attr', None)
 66         if actual is None:
 67             actual = default
 68         for default_field in default.keys():
 69             if default_field not in actual:
 70                 actual[default_field] = default[default_field]
 71         return actual
 72 
 73     def bias_attr(self):
 74         default = {'name': None, 'initializer': ConstantInitializer()}
  75         bias_attr = self.kwargs.get('bias_attr', None)
 76         if bias_attr is True:
 77             bias_attr = default
 78 
 79         if isinstance(bias_attr, dict):
 80             for default_field in default.keys():
 81                 if default_field not in bias_attr:
  82                     bias_attr[default_field] = default[default_field]
 83         return bias_attr
 84 
 85     def multiple_param_attr(self, length):
 86         param_attr = self.param_attr
 87         if isinstance(param_attr, dict):
 88             param_attr = [param_attr]
 89 
 90         if len(param_attr) != 1 and len(param_attr) != length:
 91             raise ValueError("parameter number mismatch")
 92         elif len(param_attr) == 1 and length != 1:
 93             tmp = [None] * length
 94             for i in xrange(length):
 95                 tmp[i] = copy.deepcopy(param_attr[0])
  96             param_attr = tmp
 97         return param_attr
 98 
 99     def iter_inputs_and_params(self, input_param_name='input'):
100         inputs = self.multiple_input(input_param_name)
101         param_attrs = self.multiple_param_attr(len(inputs))
102         for ipt, param_attr in itertools.izip(inputs, param_attrs):
103             yield ipt, param_attr
104 
105     def input_dtype(self, input_param_name='input'):
106         inputs = self.multiple_input(input_param_name)
107         dtype = None
108         for each in inputs:
109             if dtype is None:
110                 dtype = each.data_type
111             elif dtype != each.data_type:
112                 raise ValueError("Data Type mismatch")
113         return dtype
114 
115     def create_parameter(self, attr, shape, dtype, suffix='w',
116                          initializer=None):
117         # Deepcopy the attr so that parameters can be shared in program
118         attr_copy = copy.deepcopy(attr)
119         if initializer is not None:
120             attr_copy['initializer'] = initializer
121         if attr_copy['name'] is None:
122             attr_copy['name'] = unique_name(".".join([self.name, suffix]))
123         self.startup_program.global_block().create_parameter(
124             dtype=dtype, shape=shape, **attr_copy)
125         return self.main_program.global_block().create_parameter(
126             name=attr_copy['name'], dtype=dtype, shape=shape)
127 
128     def create_tmp_variable(self, dtype):
129         return self.main_program.current_block().create_var(
130             name=unique_name(".".join([self.name, 'tmp'])),
131             dtype=dtype,
132             persistable=False)
133 
134     def create_variable(self, *args, **kwargs):
135         return self.main_program.current_block().create_var(*args, **kwargs)
136 
137     def create_global_variable(self, persistable=False, *args, **kwargs):
138         return self.main_program.global_block().create_var(
139             *args, persistable=persistable, **kwargs)
140 
141     def set_variable_initializer(self, var,   initializer):
142         assert isinstance(var, Variable)
143         self.startup_program.global_block().create_var(
144             name=var.name,
145             type=var.type,
146             dtype=var.data_type,
147             shape=var.shape,
148             persistable=True,
149             initializer=initializer)
150 
151     def append_bias_op(self, input_var, num_flatten_dims=None):
152         """
153         Append bias operator and return its output. If the user does not set 
154         bias_attr, append_bias_op will return input_var
155          
156         :param input_var: the input variable. The len(input_var.shape) is larger
157         or equal than 2.
158         :param num_flatten_dims: The input tensor will be flatten as a matrix 
159         when adding bias.
160         `matrix.shape = product(input_var.shape[0:num_flatten_dims]), product(
161                 input_var.shape[num_flatten_dims:])`
162         """
163         if num_flatten_dims is None:
164             num_flatten_dims = self.kwargs.get('num_flatten_dims', None)
165             if num_flatten_dims is None:
166                 num_flatten_dims = 1
167 
168         size = list(input_var.shape[num_flatten_dims:])
169         bias_attr = self.bias_attr()
170         if not bias_attr:
171             return input_var
172 
173         b = self.create_parameter(
174             attr=bias_attr, shape=size, dtype=input_var.data_type, suffix='b')
175         tmp = self.create_tmp_variable(dtype=input_var.data_type)
176         self.append_op(
177             type='elementwise_add',
178             inputs={'X': [input_var],
179                     'Y': [b]},
180             outputs={'Out': [tmp]})
181         return tmp
182 
183     def append_activation(self, input_var):
184         act = self.kwargs.get('act', None)
185         if act is None:
186             return input_var
187         if isinstance(act, basestring):
188             act = {'type': act}
189         tmp = self.create_tmp_variable(dtype=input_var.data_type)
190         act_type = act.pop('type')
191         self.append_op(
192             type=act_type,
193             inputs={"X": [input_var]},
194             outputs={"Y": [tmp]},
195             attrs=act)
196         return tmp

详细的源码细节我们下一节会讲这里指写一下实现的方式和调用的函数。

作者:Charlotte77

 

相关推荐

【深度学习系列】卷积神经网络CNN原理详解(一)——基本原理(2)

94页论文综述卷积神经网络:从基础技术到研究前景

机器学习笔记(八)---- 神经网络

序列特征的处理方法之二:基于卷积神经网络方法

干货!!卷积神经网络之LeNet-5迁移实践案例

相关推荐

鸿蒙案例上新啦 | 购物、听歌、健身:15个模板带你一键激活社交娱乐

页面层级如何组织、跨页共享状态如何同步、列表与详情及个人中心如何形成稳定闭环。鸿蒙开发实践中心将这些高频消费与互动场景沉淀为可参考的标准模板,提供从业务架构到界面实现的快捷实践入口。更多实战案例欢迎前往鸿蒙开发实践中心案例中心查看并开启云端体验。

华为云官方博客 338

鸿蒙案例上新啦 | 出门打车、到家上课:25个模板带你一屏打通日常生活

如何合理拆分页面组件、如何建立单一可信的状态源、如何让用户在每一步操作中都获得明确反馈。鸿蒙开发实践中心以标准化的应用与元服务模板为入口,将清晰的业务架构与可复用的代码结构直观呈现。不妨立即挑选一个感兴趣的方向进入云端环境,从页面布局、组件封装、状态管理到路由组织,亲手跑通你的鸿蒙实战项目。

华为云官方博客 325

LakeAgent:DWS数据仓库的第二大脑,告别 “翻译式运维”,迈入智能自治时代

和普通 SQL 改写工具不同,LakeAgent 自动做多方案代价对比,主动规避负优化陷阱:补充分区键过滤实现剪枝,扫描分区由 37 收敛至 1,执行代价从 758.28 降至 113.74;未来的数据仓库,将不止能算、能存,更能看懂自己、诊断自己、理解自己。长久以来,数仓的智能都依附于人:信号需要人解读,瓶颈需要人研判,经验困在个体里,无法随底座永续生长。CPU 高、CPU 告警、CPU 诊断、CPU 使用率过高、CPU 满。语法、错误码、计费、性能调优、最佳实践、故障处置等全场景内容。

华为云官方博客 555

如何使用RDS Supabase后端BaaS能力:文件存储(File Storage)

• product-images(公开桶,允许公共读取):策略名称"Public read for product-images",允许SELECT,目标角色anon,策略定义bucket_id = 'product-images'。上传/删除需另配INSERT/DELETE策略,详见SQL。• product-specs(私有桶,仅认证用户访问自己的文件夹):建议拆为上传、读取、删除三条策略,策略定义均为 bucket_id = 'product-specs' 且仅限本人文件夹(详见下文 SQL)。

华为云官方博客 476

如何使用RDS Supabase后端BaaS能力:实时订阅与推送(Realtime)

Realtime 把数据库的每次变更同步到客户端:表里增、删、改一行,订阅者无需刷新页面或主动请求即可秒级收到。三大能力各司其职:• Postgres Changes — 监听表的 INSERT/UPDATE/DELETE,变化即通知• Broadcast — 客户端之间直接互发,适合瞬时通知、协同编辑• Presence — 跟踪频道里“谁在线”典型场景有实时数据展示(订单、库存、监控指标即时刷新)、即时通讯(消息收发、在线名单、“对方正在输入”)、协同编辑与实时通知。

华为云官方博客 465

AI Agent如何通过MCP连接到华为云RDS Supabase应用平台

Schema元数据列出所有可访问的表(按schema分组)。Schema元数据列出表的列及元数据(类型/默认值/约束)。Schema元数据列出所有约束(PK/FK/UNIQUE/CHECK/EXCLUDE)。可按schema/表/类型过滤。Schema元数据列出所有外键关系。可按schema/表过滤。Schema元数据列出所有索引及定义和大小。可按schema/表过滤。Schema元数据列出已应用的数据库迁移记录。Schema元数据列出已安装的PostgreSQL扩展。

华为云官方博客 473

华为大数据Flink让每一 CU 算力,跑出开源 5.74 倍的性能

1. 开源Apache Flink 已成为业界流计算事实标准,但在极端重负载场景下存在性能瓶颈。2. 华为大数据Flink通过多项自研深度优化,在执行计划、算子执行、状态存储三个层面构建协同的优化体系。3. Nexmark Benchmark 测试结果:整体性能提升 5.74 倍,极端重负载场景性能领先最高 18.3 倍。4. 100% 兼容 Apache Flink 1.20,业务代码零改造,无缝迁移。

华为云官方博客 380

LakeAgent实战 | 业务分析篇从一句提问到业务分析

本文以“订单金额变化”分析为例,展示LakeAgent如何通过自然语言提问,结合DataArtsStudio的数据资产与专业技能,帮助业务人员快速定位数据、生成SQL、追踪血缘、生成报告。从问题出发,逐步实现数据发现、变化洞察、原因追溯与团队协作,最终形成可复用的分析流程与企业规则,显著提升数据分析效率与一致性。

华为云官方博客 390

华为云RDS Supabase AI应用已开放公测 :30分钟搭建AI应用后端

认证、存储、实时推送、数据库API——4个后端模块,传统开发需要数周。今天,你只需要30分钟。AI创业公司的全栈开发工程师。上周老板说"下周要上线的AI对话应用,后端你来搞"——认证鉴权、数据库接口、文件存储、实时推送,每一个模块都需要数周搭建,而市场不会等你。小张打开华为云RDS Supabase,30分钟搭好认证、数据库API、对象存储,剩下的时间全用来打磨AI交互体验。基于开源Supabase+华为云RDS PostgreSQL全托管能力,为开发者提供。

华为云官方博客 396

如何使用RDS Supabase后端BaaS能力:数据库函数(Database Function)

上一篇介绍了 Database Data API——建表即自动生成增删改查接口,解决单表读写,但聚合统计、跨表事务、字段自动维护等逻辑已超出其能力边界。本篇推进到数据库函数(Database Function):把业务逻辑封装进数据库,前端通过 .rpc() 调用,由触发器自动响应数据变更。上一篇中,小张基于 Data API 完成了产品目录的建表与基础读写。但新需求随之而来:按分类统计产品数与价格区间、上架新品同时写产品表与定价表、产品修改后自动刷新更新时间。

华为云官方博客 464

如何使用RDS Supabase后端BaaS能力:创表即生成API(Database Data API)

认证、存储、实时推送、数据库API——4个后端模块,传统开发要数周。本系列逐个拆解,首篇聚焦数据库API(Database Data API):建表即得 API,无需手写后端。小张,AI创业公司的全栈工程师。老板让他做个产品目录页,展示产品与价格、支持上下架。传统做法要搭后端、写接口、配权限,工期少说1-2周;而在华为云RDS Supabase,建两张表、应用 SDK 直连,几分钟增删改查全跑通。

华为云官方博客 478

从一句话到一个能出去的链接,AI开发者空间做了什么

自行搭建的 Agent、正在使用的开源项目、团队既有的工具,都可以接入,可以通过AI创建或者手动搭建环境;华为云 AI开发者空间——面向开发者的云端一站式开发平台,做的就是把这段路接上:环境免安装,浏览器打开即为完整的 Linux 运行环境,预置的 AI 智能体开箱即用。第一个任务建议选能出结果的:做一个游戏、生成一个页面、创建一台实例,从需求走到可访问的链接,一次就能看清这个环境在做什么。沙箱是一个完整的 Linux 环境,依赖、服务、端口都是真实的,不是模拟。环境释放后,权限随之失效。

华为云官方博客 426

华为云码道配置自定义大模型,四种方式完整实操

案例介绍。

华为云官方博客 624

图数据库:实现穿透式监管的数据基座

穿透式监管标志着国企监管从“管企业”向“管资本”转型过程中的关键一跃。而图数据库正是这一跃的技术支点。从股权穿透到资金链穿透,从供应链穿透到数据穿透,华为GES图数据库正在将“穿透式监管”从理念变为可落地、可操作的实践。无论是天眼查式的全量链路查询,还是某集团财经的税务规则判断,抑或是芯片供应链式的部件风险分析,乃至通过Palantir构建决策知识图谱,其底层逻辑都是一致的——用图的力量,穿透一切形式的“隔离层”,直抵风险本质与责任核心。

华为云官方博客 664

《公共云三十问》正式收官,奔赴企业AI百场景新征程

今年6月底,公共云产业联盟重点推出《公共云三十问》专栏,以每周三问的节奏,连载十周,解读《先进公共云》白皮书,分享国计民生系列案例,旨在凝聚共识、推动落地实践。同时,专栏分析公共云在产业升级、民生福祉、科技创新、全球发展等方面的作用,梳理中国特色公共云发展路径:以普惠云拓展用云广度,以智算云提升用云深度,以自主云夯实用云厚度,以全球云打开用云跨度。这十问从专属云、行业云切入,深入政务、金融、交通、电力、钢铁、化工、油气、制造等国计民生场景,回答公共云“怎么用”。

华为云官方博客 473

开发者小技巧|IAM配置,让码道代码智能体连续30天免登录

案例介绍。

华为云官方博客 645

码道深度对接CodeArts,让需求自动落地为代码

案例介绍。

华为云官方博客 720

【公共云三十问 之三十】拥抱AI,企业如何建设面向智能体的专属云?

企业可分阶段建设覆盖开发、运行、运营及安全的一体化智能体平台——从构建一站式、高低码协同的智能体开发平台,到打造高性能、极致弹性的运行时环境,再到搭建AgentOps驱动的持续运营与运维体系,同时构建端到端、多层次的全链路安全防护体系。在数据湖建设方面,遵循“存量复用、平滑演进、AI导向、分步迭代”原则,分三阶段推进——打造AI多模数据湖核心能力、完成存算分离与统一元数据管理、实现闭环赋能全面支撑智能体规模化落地,在保护既有投资的前提下将传统数据平台转型为AI数据专属供给平台。

华为云官方博客 823

【公共云三十问 之二十九】国计民生之制造:制造云如何重塑数智时代的新质生产力?

平台打通软件交付的完整路径,覆盖需求管理、代码托管、代码检查、编译构建、制品仓库、部署、测试等关键环节,支持大模型能力融入研发全流程,实现从需求到交付的端到端管理。通过构建“研发一朵云”,方案打破地域与组织壁垒,实现跨团队协同开发,有效缩短软件交付周期,助力工业制造企业从“传统研发”走向“敏捷研发”,加速数智化转型进程。当制造云真正成为数据流通的平台、AI创新的底座,公共云的社会价值将从基础设施层向生产场景层持续延伸,为新型工业化建设和制造强国战略注入源源不断的动力,重塑数智时代的中国制造新质生产力。

华为云官方博客 789

告别重复手工迁仓,码道MCP+Skill实现仓库批量迁移

案例介绍。

华为云官方博客 790

【公共云三十问 之二十八】国计民生之油气:智算云如何叩响数智时代的“地心蓝金”?

西南油气田以“向地球深部要资源”为战略方向,联合以华为云Stack为代表的公共云服务商共同打造统一算力底座,建设西南区域数据湖与通用技术平台,构建“勘探开发工程技术、天然气生产过程管控、天然气产运储销”等三个一体化协同应用场景,支撑油气勘探、开发生产、管输储运等全业务链的数智化转型。当智算云真正成为数据流通的平台、AI创新的底座,油气田得以将算力效能、数据价值与智能技术深度融入勘探开发全链条,将“看不见的算力”转化为“摸得着的储量”,以智能化手段持续降低勘探风险、缩短发现周期、提高采收效率,确保生产安全。

华为云官方博客 613

【公共云三十问 之二十七】国计民生之化工:数智云如何重塑传统化工新范式?

在架构方面,方案构建云边协同的统一体系,涵盖AI协同、数据协同与运营协同三大维度——总部集中训练大模型,边缘节点负责推理执行,云边持续迭代,实现“边用边学”的智能闭环,模型平均精度提升20%以上。此外,方案提供一站式企业级工业物联网平台、数据湖与数据治理平台,覆盖工业数据分析“采、存、算、管、用”全流程,打通生产现场与管理系统间的数据壁垒,有效消除数据孤岛。层面,方案充分发挥软硬协同优势,实现计算、存储、网络与云平台的深度协同,训练线性度超95%,为大模型训练推理提供强劲算力支撑。▲化工“数智”新模式。

华为云官方博客 663

【公共云三十问 之二十六】国计民生之钢铁:数智云如何淬炼中国钢铁新力量?

同时,构建云边端协同的统一架构,支撑集团总部与各生产基地两级协同发展——总部集中训练大模型,边缘节点负责推理执行,云边持续迭代,实现训推一体化的资源调度与高效协同,为钢铁行业的数智化转型构建了坚实的算力底座。层面,方案构建覆盖“工业数采→边缘IT/OT融合→统一数据湖→数据治理平台→数据应用”全链路的一站式数据治理与流通平台,打通生产现场与管理系统间的数据壁垒,让数据“采得到、流得动、用得好”,加速数据价值从生产现场向经营决策层流动释放。湘潭钢铁集团的钢铁数智云,是钢铁工业智能云建设的标杆案例。

华为云官方博客 526

LakeWatch——MRS 智能运维Agent

从资源池、队列、作业等多个维度,对集群CPU与内存资源进行精细化分析与对比,直观呈现不同时间段的空闲、满载等使用状态,识别资源浪费,指导治理优化,为扩缩容决策提供数据支撑。对HDFS存储进行多维度深度分析(目录、用户、用户组等),涵盖使用量、增量、文件大小分布及数据热度等指标,帮助快速识别增长较快的目录、长期未访问的文件,统计目录文件数量与小文件占比,评估HDFS总存储量与扩缩容需求,为存储治理与资源规划提供全面数据支撑。Agent主动巡检,识别异常趋势并提前预警,让运维从"事后处理"转向"事前预防"。

华为云官方博客 515

华为云专题文档发布:丰富的AI案例、选型指导和概念解读

发布 “云服务选型”、“云计算概念”专题,提供云服务选型指导,助您选择匹配业务的云服务,解读云计算相关概念,开启云上学习之路。开发者工具和文档专区上线,提供从学习、实践到反馈的全链路支持。帮助中心新增100余篇AI案例。

华为云官方博客 286

一个人,也能用AI做点“星东西”

华为云星耀创想者大赛-2026华为云OPC创意大赛等你来~

华为云官方博客 290

从需求到可执行工作项:MCP+Skill 让智能体规划真正落地

回顾整个体验,以前让智能体帮我拆需求,它是洋洋洒洒给我列了一大篇 Markdown,看着挺像那么回事,但最后还是得我一条条往 CodeArts Req 里搬,搬完再手动挂父子关系、排日期,拆需求五分钟,搬运两小时。现在好了,MCP 给智能体接上了"手",Skill 给它装上了"项目经理的脑子",一句话下去,34 个工作项连带排期、层级关系全给我建好了,甘特图一拉,齐刷刷都在两周内,节省了多少工作量啊,美滋滋。本次实操在码道代码智能体基础上,构建了一套"需求→可执行工作项"的自动化规划方案。

华为云官方博客 576

华为云码道「码力续航计划」更新!

✅上新GLM‑5.3‑Flash,高速响应,代码生成、工程解析、Agent调试高效输出。新增GLM‑5.3‑Flash福利模型,每日解锁1000万免费Tokens。● 本次活动共发放总量价值100万元Tokens,发完即止!把耗算力的编码重活,交给华为云码道代码智能体🚀🚀。● 每日额度准时刷新,当日清零、次日满血重置。✅复杂算法、大项目解析、批量代码生成随便跑。✅不用抠额度、不用算消耗,放开调试大胆迭代。✅无需额外操作,登录即送,直接切换模型使用。● 把算力交给码道,让撸码不再内耗👏🏻。

华为云官方博客 687

【公共云三十问 之二十四】国计民生之交通:交通云如何铺就数智通衢新坦途?

交通行业作为支撑实体经济的关键基础设施,涵盖公路、铁路、机场、港口、地铁等众多子领域,其中,公路网络作为综合交通运输体系的基础骨架,承担着全国超过70%的客货运量,其数字化、智能化转型的需求尤为迫切。方面,提供Agent开发框架、知识库管理、RAG工程、意图识别、静态应用编排及模型评测等能力,支持客户将行业知识、业务流程与AI能力深度融合,快速构建面向收费稽核、路网调度、应急处置等场景的智能应用。开发并实现落地应用,全面覆盖建设、管理、养护、运营、服务、安全、办公七大业务领域。

华为云官方博客 461

【公共云三十问 之二十五】国计民生之电力:电力云如何点亮数智电力新引擎?

方案构建了覆盖数据获取、质量评估到标准化处理的全链路能力。基于公共云服务商智算底座及气象大模型,通过时空图神经网络重新建模产流机制,中国华电建成超50TB多源融合数据集,突破时空建模与机理融合等五大关键技术,短期径流预测精度较传统机理模型提升5个百分点以上,实现15天中期径流预测精度超85%,推理效率达秒级。基于气象大模型的基础架构与预训练能力,客户可直接在大模型之上开展增量训练与微调,充分利用场站所在区域的高精度历史数据(如3公里、1公里分辨率),针对不同场站和区域的地形地貌与气候特征进行模型调优。

华为云官方博客 381
上一篇: 开发者如何学好 MongoDB
下一篇: 【云速建站】让你轻轻松松建立属于自己的网店
华为云开发者联盟
华为云开发者联盟 企业官方账号 企业官方账号
博客等级 码龄10年 72万+粉丝 5985原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值