TensorFlow-Keras - 一文搞懂 TF 常用矩阵计算方法

news2025/1/15 7:25:00

目录

一.引言

二.tf.multiply

1.常规乘法

2.乘以标量

3.不规则乘法

三.tf.matmul

1.常规矩阵相乘

2.多维矩阵相乘

四.tf.tensordot

1.axes=1

2.axes=N

3.axes=Tuple

4.axes=Array(Tuple())

五.K.dot

六.K.batch_dot

1.不指定 axes

2.指定 axes 为数字

3.指定 axes 为列表

4.二者维度不一致

七.tf.einsum

1.求和操作

2.矩阵乘法

3.向量对位相乘

4.向量 dot

5.全部求和


一.引言

开发深度学习任务时经常涉及到矩阵、向量的运算,例如 tf.multiply、tf.matmul、tf.tensordot、K.dot、K.batch_dot 以及 tf.einsum 等等,面对复杂的维度再加上五花八门的 API 时常处于眩晕状态,下面整理下常用矩阵计算方法示例。

二.tf.multiply

该方法用于计算哈达玛乘积,其实就是我们常说的对位相乘,其和 * 在一些场景下计算相同。给定两个向量,如果使用 multiply 就是对位相乘,如果是 dot 就是把对位相乘的结果加起来。其次如果维度的子集与待乘元素维度匹配,依旧可以对位相乘,所以该方法对维度要求较宽松。FM 的二阶项计算和平方与平方和就涉及到矩阵的对位相乘。

1.常规乘法

    mat1 = tf.reshape(tf.constant([1] * 12), [3, 4])
    mat2 = tf.reshape(tf.constant([2] * 12), [3, 4])

    print(mat1 * mat2)
    print(tf.multiply(mat1, mat2))

可以看到 multiply 与 * 结果相同,因为是对位相乘,所以 MxN * MxN => MxN 

tf.Tensor(
[[2 2 2 2]
 [2 2 2 2]
 [2 2 2 2]], shape=(3, 4), dtype=int32)
tf.Tensor(
[[2 2 2 2]
 [2 2 2 2]
 [2 2 2 2]], shape=(3, 4), dtype=int32)

2.乘以标量

    print(tf.multiply(mat1, 100))

标量直接作用与  MxN 的矩阵的每一个元素 Xij 

tf.Tensor(
[[100 100 100 100]
 [100 100 100 100]
 [100 100 100 100]], shape=(3, 4), dtype=int32)

3.不规则乘法

    mat2 = tf.reshape(tf.constant([2] * 12), [3, 4])
    mat3 = tf.constant([2] * 4)
    print(mat3)
    print(tf.multiply(mat2, mat3))

mat2 为 3x4,mat3 为 4,二者相乘的到 3x4。同理,如果前者为 MxNxK,后者为 NxK,multiply 会把 NxK 的部分对位相乘,然后得到 MxNxK。 

tf.Tensor([2 2 2 2], shape=(4,), dtype=int32)
tf.Tensor(
[[4 4 4 4]
 [4 4 4 4]
 [4 4 4 4]], shape=(3, 4), dtype=int32)

 

三.tf.matmul

matmul 矩阵常规计算语法

1.常规矩阵相乘

    mat1 = tf.reshape(tf.constant([1] * 12), [3, 4])
    mat2 = tf.reshape(tf.constant([2] * 12), [4, 3])
    print(tf.matmul(mat1, mat2))

matmul MxN NxK => MxK,前者的 dim[-1] 与后者的 dim[0] 匹配即可。

tf.Tensor(
[[8 8 8]
 [8 8 8]
 [8 8 8]], shape=(3, 3), dtype=int32)

2.多维矩阵相乘

    mat1 = tf.reshape(tf.constant([1] * 24), [2, 3, 4])
    mat2 = tf.reshape(tf.constant([2] * 24), [2, 4, 3])
    print(tf.matmul(mat1, mat2))

matmul MxNxK MxKxP => MxNxP,二者的首维相同,该方法主要用于两个 Batch 的样本执行矩阵计算,其中 M 看做 None,后面 matmul NxK KxP => NxP。FmFM 的 Fm 部门与 matrix 相乘就用到该方法。

tf.Tensor(
[[[8 8 8]
  [8 8 8]
  [8 8 8]]

 [[8 8 8]
  [8 8 8]
  [8 8 8]]], shape=(2, 3, 3), dtype=int32)

四.tf.tensordot

tf.tensordot 相对于前面的 multiply 或者 matmul 更加平凡或者说更加灵活,其提供 axes 参数,矩阵相乘只需 axes 指定的维度相等即可。

1.axes=1

默认 [-1,0],axes 代表前者的 dim[-1] 与后者的 dim[0] 对应,运算后对应维度消失。

    mat1 = tf.ones(shape=[2, 4, 3])
    mat2 = tf.ones(shape=[3, 2, 4])
    print(tf.tensordot(mat1, mat2, axes=1))

M x N x K * K x P x Q => M x N x P x Q,前后的 K 都消失。

tf.Tensor(
[[[[3. 3. 3. 3.]
   [3. 3. 3. 3.]]

  [[3. 3. 3. 3.]
   [3. 3. 3. 3.]]

  [[3. 3. 3. 3.]
   [3. 3. 3. 3.]]

  [[3. 3. 3. 3.]
   [3. 3. 3. 3.]]]


 [[[3. 3. 3. 3.]
   [3. 3. 3. 3.]]

  [[3. 3. 3. 3.]
   [3. 3. 3. 3.]]

  [[3. 3. 3. 3.]
   [3. 3. 3. 3.]]

  [[3. 3. 3. 3.]
   [3. 3. 3. 3.]]]], shape=(2, 4, 2, 4), dtype=float32)

2.axes=N

前者的后两维与后者的前两维相同,相乘后对应维度消失。

    mat1 = tf.ones(shape=[2, 4, 3])
    mat2 = tf.ones(shape=[3, 4, 4])
    print(tf.tensordot(mat1, mat2, axes=2))

看了 axes=1、2 的示例,这里也可以推广至 axes=N,大家可以自己尝试。 

tf.Tensor(
[[12. 12. 12. 12.]
 [12. 12. 12. 12.]], shape=(2, 4), dtype=float32)

3.axes=Tuple

Tuple(_.1,_.2) axes 为元组时,._1 代表前者维度索引,._2 代表后者维度索引,要求 _.1 = _.2,相乘后对应维度消失。

    mat1 = tf.ones(shape=[2, 4, 3])
    mat2 = tf.ones(shape=[3, 2, 4])
    print(tf.tensordot(mat1, mat2, axes=(1, 2)))

2x4x3 的索引1维度为 4,3x2x4的索引2维度为4,二者相乘对应维度消失其余保留得到 2x3x3x2。 

tf.Tensor(
[[[[4. 4.]
   [4. 4.]
   [4. 4.]]

  [[4. 4.]
   [4. 4.]
   [4. 4.]]

  [[4. 4.]
   [4. 4.]
   [4. 4.]]]


 [[[4. 4.]
   [4. 4.]
   [4. 4.]]

  [[4. 4.]
   [4. 4.]
   [4. 4.]]

  [[4. 4.]
   [4. 4.]
   [4. 4.]]]], shape=(2, 3, 3, 2), dtype=float32)

4.axes=Array(Tuple())

这个其实和 axes=1 推广至 axes=N 比较类似,这里要求前者的 ._1 * ._1 = ._2 * ._2 

    mat1 = tf.ones(shape=[2, 4, 3])
    mat2 = tf.ones(shape=[3, 2, 4])
    print(tf.tensordot(mat1, mat2, axes=((1, 2), (0, 2))))

上面说的比较抽象,下面玩点真实的,(1, 2)、(0, 2) 前者维度 4*3 = 12 后者维度 3*4 = 12,对应维度消失,得到 2*2。虽然理解用法了,但是这里维度太多还是不好想象出来真实变换场景。axes 方法在 DCN 构建 CrossLayer 时用到。

tf.Tensor(
[[12. 12.]
 [12. 12.]], shape=(2, 2), dtype=float32)

五.K.dot

广义矩阵相乘,这里限制较多且无法指定维度,要求前者 dim[-1] 与 后者 dim[-2] 相等,可以看做是 tf.tensordot 的特例,即 tf.tensordot(mat1, mat2, axes=(-1, -2))。通过其定义我们也可以看出其主要用于 TF lookup 得到向量后与参数矩阵相乘使用,第一个维度看做是 None,然后用后面的矩阵相乘。

    mat1 = tf.ones(shape=[2, 4, 3])
    mat2 = tf.ones(shape=[6, 3, 4])
    print(K.dot(mat1, mat2))
    print(tf.tensordot(mat1, mat2, axes=(-1, -2)))

2x4x3 的最后一维是3,6x3x4 的倒数第二维是3,相乘然后对位维度消失得到 2x4x6x4,大家可以用 tf.tensordot axes=(-1,-2) 验证数据是否一致。

tf.Tensor(
[[[[3. 3. 3. 3.]
   [3. 3. 3. 3.]
   [3. 3. 3. 3.]
   [3. 3. 3. 3.]
   [3. 3. 3. 3.]
   [3. 3. 3. 3.]]

  [[3. 3. 3. 3.]
   [3. 3. 3. 3.]
   [3. 3. 3. 3.]
   [3. 3. 3. 3.]
   [3. 3. 3. 3.]
   [3. 3. 3. 3.]]

  [[3. 3. 3. 3.]
   [3. 3. 3. 3.]
   [3. 3. 3. 3.]
   [3. 3. 3. 3.]
   [3. 3. 3. 3.]
   [3. 3. 3. 3.]]

  [[3. 3. 3. 3.]
   [3. 3. 3. 3.]
   [3. 3. 3. 3.]
   [3. 3. 3. 3.]
   [3. 3. 3. 3.]
   [3. 3. 3. 3.]]]


 [[[3. 3. 3. 3.]
   [3. 3. 3. 3.]
   [3. 3. 3. 3.]
   [3. 3. 3. 3.]
   [3. 3. 3. 3.]
   [3. 3. 3. 3.]]

  [[3. 3. 3. 3.]
   [3. 3. 3. 3.]
   [3. 3. 3. 3.]
   [3. 3. 3. 3.]
   [3. 3. 3. 3.]
   [3. 3. 3. 3.]]

  [[3. 3. 3. 3.]
   [3. 3. 3. 3.]
   [3. 3. 3. 3.]
   [3. 3. 3. 3.]
   [3. 3. 3. 3.]
   [3. 3. 3. 3.]]

  [[3. 3. 3. 3.]
   [3. 3. 3. 3.]
   [3. 3. 3. 3.]
   [3. 3. 3. 3.]
   [3. 3. 3. 3.]
   [3. 3. 3. 3.]]]], shape=(2, 4, 6, 4), dtype=float32)

六.K.batch_dot

批量广义矩阵相乘,可以通过 axes 指定维度,两个输入的维度相等即可。如果不指定 axes,则类似 K.dot 要求 x_dim - 1 与 y_dim - 2 即前者倒1等于后者倒2。这里同样可以将 y_dim - 2 前面的维度看做 batch_size 的 None 从而实现 emb 与参数矩阵相乘,在 FwFM Fw 加权部分中用到了该方法。

1.不指定 axes

    mat1 = tf.ones(shape=[8, 4, 3])
    mat2 = tf.ones(shape=[8, 3, 4])
    print(K.batch_dot(mat1, mat2))

需要注意的是,这里相乘后不再是对位消失其余保留了即 8x4x8x4,而是8保留,4x3 与 3x4 执行矩阵计算,最后得到 8 x 4 x 4。换成符号语言就是 MxNxK 与 MxKxP 相乘的到 M x N x P,对应维度消失,首维度不变。

tf.Tensor(
[[[3. 3. 3. 3.]
  [3. 3. 3. 3.]
  [3. 3. 3. 3.]
  [3. 3. 3. 3.]]

 [[3. 3. 3. 3.]
  [3. 3. 3. 3.]
  [3. 3. 3. 3.]
  [3. 3. 3. 3.]]

 [[3. 3. 3. 3.]
  [3. 3. 3. 3.]
  [3. 3. 3. 3.]
  [3. 3. 3. 3.]]

 [[3. 3. 3. 3.]
  [3. 3. 3. 3.]
  [3. 3. 3. 3.]
  [3. 3. 3. 3.]]

 [[3. 3. 3. 3.]
  [3. 3. 3. 3.]
  [3. 3. 3. 3.]
  [3. 3. 3. 3.]]

 [[3. 3. 3. 3.]
  [3. 3. 3. 3.]
  [3. 3. 3. 3.]
  [3. 3. 3. 3.]]

 [[3. 3. 3. 3.]
  [3. 3. 3. 3.]
  [3. 3. 3. 3.]
  [3. 3. 3. 3.]]

 [[3. 3. 3. 3.]
  [3. 3. 3. 3.]
  [3. 3. 3. 3.]
  [3. 3. 3. 3.]]], shape=(8, 4, 4), dtype=float32)

2.指定 axes 为数字

    mat1 = tf.ones(shape=[8, 3, 4])
    mat2 = tf.ones(shape=[8, 3, 4])
    print(K.batch_dot(mat1, mat2, axes=1))

这里要 Dot 的 axes 会消失,即 3 消失 变成 8 x 4 x 4。

tf.Tensor(
[[[3. 3. 3. 3.]
  [3. 3. 3. 3.]
  [3. 3. 3. 3.]
  [3. 3. 3. 3.]]

 [[3. 3. 3. 3.]
  [3. 3. 3. 3.]
  [3. 3. 3. 3.]
  [3. 3. 3. 3.]]

 [[3. 3. 3. 3.]
  [3. 3. 3. 3.]
  [3. 3. 3. 3.]
  [3. 3. 3. 3.]]

 [[3. 3. 3. 3.]
  [3. 3. 3. 3.]
  [3. 3. 3. 3.]
  [3. 3. 3. 3.]]

 [[3. 3. 3. 3.]
  [3. 3. 3. 3.]
  [3. 3. 3. 3.]
  [3. 3. 3. 3.]]

 [[3. 3. 3. 3.]
  [3. 3. 3. 3.]
  [3. 3. 3. 3.]
  [3. 3. 3. 3.]]

 [[3. 3. 3. 3.]
  [3. 3. 3. 3.]
  [3. 3. 3. 3.]
  [3. 3. 3. 3.]]

 [[3. 3. 3. 3.]
  [3. 3. 3. 3.]
  [3. 3. 3. 3.]
  [3. 3. 3. 3.]]], shape=(8, 4, 4), dtype=float32)

3.指定 axes 为列表

   mat1 = tf.ones(shape=[8, 4, 3])
   mat2 = tf.ones(shape=[8, 3, 4]) 
   print(K.batch_dot(mat1, mat2, axes=[2, 1]))

结果以及计算思路与上面不指定 axes 相同。 

tf.Tensor(
[[[3. 3. 3. 3.]
  [3. 3. 3. 3.]
  [3. 3. 3. 3.]
  [3. 3. 3. 3.]]

 [[3. 3. 3. 3.]
  [3. 3. 3. 3.]
  [3. 3. 3. 3.]
  [3. 3. 3. 3.]]

 [[3. 3. 3. 3.]
  [3. 3. 3. 3.]
  [3. 3. 3. 3.]
  [3. 3. 3. 3.]]

 [[3. 3. 3. 3.]
  [3. 3. 3. 3.]
  [3. 3. 3. 3.]
  [3. 3. 3. 3.]]

 [[3. 3. 3. 3.]
  [3. 3. 3. 3.]
  [3. 3. 3. 3.]
  [3. 3. 3. 3.]]

 [[3. 3. 3. 3.]
  [3. 3. 3. 3.]
  [3. 3. 3. 3.]
  [3. 3. 3. 3.]]

 [[3. 3. 3. 3.]
  [3. 3. 3. 3.]
  [3. 3. 3. 3.]
  [3. 3. 3. 3.]]

 [[3. 3. 3. 3.]
  [3. 3. 3. 3.]
  [3. 3. 3. 3.]
  [3. 3. 3. 3.]]], shape=(8, 4, 4), dtype=float32)

4.二者维度不一致

    mat1 = tf.ones(shape=[8, 4, 8])
    mat2 = tf.ones(shape=[8, 8])
    print(K.batch_dot(mat1, mat2))

mat1 看做是 lookup 的批次样本 None x Field x EmdDim 后者为参数矩阵 EmdDim x EmdDim,再次计算时保留 mat1 的 batch_size 即 None,这里会做 sumed 处理,所以最后得到 8 x 4。

tf.Tensor(
[[8. 8. 8. 8.]
 [8. 8. 8. 8.]
 [8. 8. 8. 8.]
 [8. 8. 8. 8.]
 [8. 8. 8. 8.]
 [8. 8. 8. 8.]
 [8. 8. 8. 8.]
 [8. 8. 8. 8.]], shape=(8, 4), dtype=float32)

这里不好理解的话也可以看下官方给的示例:

 

七.tf.einsum

爱因斯坦求和约定,这里使用更加 common 的表达式方式制定维度。

1.求和操作

    mat1 = tf.ones(shape=[8, 4, 8])
    mat2 = tf.einsum("ijk->ij", mat1)
    print(mat2)

相当于 reduce_sum,将给定矩阵的第三维累加起来,如果换做数学语言:

B_{ij} = \sum_{k}^{}Aijk 

即将 K 维的数字累加,如果换做是程序语言:

    i, j, k = mat1.shape[0], mat1.shape[1], mat1.shape[2]
    mat1 = np.ones(shape=[8, 4, 8])
    mat2 = np.zeros(shape=[i, j])
    for i_ in range(i):
        for j_ in range(j):
            for k_ in range(k):
                mat2[i_][j_] += mat1[i_][j_][k_]
    print(mat2)

对哪个 axis 求和,哪个维度消失,所以 8x4x8 对 ijk 的 k 即第三维求和所以剩 ij 即 8x4。

[[8. 8. 8. 8.]
 [8. 8. 8. 8.]
 [8. 8. 8. 8.]
 [8. 8. 8. 8.]
 [8. 8. 8. 8.]
 [8. 8. 8. 8.]
 [8. 8. 8. 8.]
 [8. 8. 8. 8.]]

2.矩阵乘法

    mat1 = tf.ones(shape=[2, 10, 8])
    mat2 = tf.ones(shape=[8, 8])
    mat3 = tf.einsum("bij,mn->bin", mat1, mat2)
    print(mat3)

矩阵乘法 None[b] x Filed[i] x EmdDim[j] EmbDim[j] x EmdDim[k] => None x Filed x EmdDim [bik]

相当于 matmul,数学语言:

C_{bin}=\sum_{j} \sum_{m} A_{bij} * B_{mn}

消失的维度放到求和符号中,剩下的维度保留,最终维度 2 x 10 x 8,程序语言:

    b, i, j = mat1.shape[0], mat1.shape[1], mat1.shape[2]
    m, n = mat2.shape[0], mat2.shape[1]
    mat3 = np.zeros(shape=[b, i, j])
    for b_ in range(b):
        for i_ in range(i):
            for j_ in range(j):
                for m_ in range(m):
                    for n_ in range(n):
                        mat3[b_][i_][j_] += mat1[b_][i_][j_] * mat2[m_][n_]
    print(mat3)
tf.Tensor(
[[[64. 64. 64. 64. 64. 64. 64. 64.]
  [64. 64. 64. 64. 64. 64. 64. 64.]
  [64. 64. 64. 64. 64. 64. 64. 64.]
  [64. 64. 64. 64. 64. 64. 64. 64.]
  [64. 64. 64. 64. 64. 64. 64. 64.]
  [64. 64. 64. 64. 64. 64. 64. 64.]
  [64. 64. 64. 64. 64. 64. 64. 64.]
  [64. 64. 64. 64. 64. 64. 64. 64.]
  [64. 64. 64. 64. 64. 64. 64. 64.]
  [64. 64. 64. 64. 64. 64. 64. 64.]]

 [[64. 64. 64. 64. 64. 64. 64. 64.]
  [64. 64. 64. 64. 64. 64. 64. 64.]
  [64. 64. 64. 64. 64. 64. 64. 64.]
  [64. 64. 64. 64. 64. 64. 64. 64.]
  [64. 64. 64. 64. 64. 64. 64. 64.]
  [64. 64. 64. 64. 64. 64. 64. 64.]
  [64. 64. 64. 64. 64. 64. 64. 64.]
  [64. 64. 64. 64. 64. 64. 64. 64.]
  [64. 64. 64. 64. 64. 64. 64. 64.]
  [64. 64. 64. 64. 64. 64. 64. 64.]]], shape=(2, 10, 8), dtype=float32)

3.向量对位相乘

    vec1 = tf.constant([1] * 12)
    vec2 = tf.constant([2] * 12)
    vec3 = tf.einsum("i,i->i", vec1, vec2)
    print(vec3)

 i,i -> i 实现 mutiply 对位相乘

tf.Tensor([2 2 2 2 2 2 2 2 2 2 2 2], shape=(12,), dtype=int32)

4.向量 dot

    vec1 = tf.constant([1] * 12)
    vec2 = tf.constant([2] * 12)
    vec3 = tf.einsum("i,i->", vec1, vec2)
    print(vec3)

i,i -> 实现向量内积

tf.Tensor(24, shape=(), dtype=int32)

5.全部求和

    mat1 = tf.ones(shape=[2, 10, 8])
    sum_ = tf.einsum("ijk->", mat1)
    print(sum_)

不管多少维,-> 后面均不指定,最终得到求和结果。

tf.Tensor(160.0, shape=(), dtype=float32)

Tips:

上面展示了一些常规的使用方法,具体到实战中可以使用不同语法实现相同功能,更完整的功能介绍与维度变换打击可以直接看官方源码,都有完整的解释与示例:

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

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

相关文章

【观察】连续八年霸榜云数据库“领导者”,揭秘亚马逊云科技背后的“统治力”...

日前,全球市场分析机构 Gartner发布《2022 云数据库管理系统魔力象限》报告。其中,在Gartner本次魔力象限报告评估的20家供应商中,亚马逊云科技在纵轴“执行能力”和横轴“愿景完整性”两个维度分别处于最高、最右位置,这也是亚马…

Zigbee物联网组网

物联网的核心和基础是互联网,物联网是在互联网基础上的延伸和扩展的网络,然而在物联网当中基于海量数据的无线传感网是物联网极具代表的网络之一,其用户端延伸和扩展到了任何物品与物品之间,进行信息交换和通信。 硬件设备及连接&…

PPC Insights系列:洞见安全多方图联邦

开放隐私计算开放隐私计算开放隐私计算OpenMPC是国内第一个且影响力最大的隐私计算开放社区。社区秉承开放共享的精神,专注于隐私计算行业的研究与布道。社区致力于隐私计算技术的传播,愿成为中国 “隐私计算最后一公里的服务区”。183篇原创内容公众号知…

NetCore使用SkyWalking

官网中文文档:SkyWalking 极简入门 | Apache SkyWalking一、引用依赖新建一个项目:Cbf.SkyWalking.ServiceInstancenuget安装:SkyAPM.Agent.AspNetCore二、launchSettings.json添加这两行配置或者在这里添加这两行也行:三、需要添…

界面组件DevExpress WinForms v22.2 - 全面升级数据展示功能

DevExpress WinForms拥有180组件和UI库,能为Windows Forms平台创建具有影响力的业务解决方案。DevExpress WinForms能完美构建流畅、美观且易于使用的应用程序,无论是Office风格的界面,还是分析处理大批量的业务数据,它都能轻松胜…

某马程序员NodeJS速学笔记

文章目录前言一、什么是Node.js?二、fs文件系统模块三、Http模块四、模块化五、开发属于自己的包模块加载机制六、Express1.初识ExpressGET/POSTnodemon2.路由模块化3.中间件中间件分类自定义中间件4. 跨域问题七、Mysql模块安装与配置基本使用Web开发模式Session认证JWT八、大…

MySQL查询操作

系列文章目录前言一、简单查询SELECT子句SELECT后面之间跟列名DISTINCT,ALL列表达式列更名WHERE子句WHERE子句中可以使用的查询条件比较运算BETWEEN...AND...集合查询:IN模糊查询LIKE空值比较:IS NULL多重条件查询SELECT 的基本结构ORDER BY子句排序聚集…

vue导出excel

1、下载依赖 npm install --save xlsx file-saverps:我下载完依赖后再运行会报错,偶尔情况,没找到原因,只需要卸载重新npm install就好 2、新建一个写公共js方法的文件**,如果你有的话 ,写在里面就好 然…

Mysql 用户管理、权限管理(含用户密码修改)

和Linux 系统一样,也有着自己独有的用户管理系统,MySQL所有的用户信息都被保存在mysql数据库中的user表中。 目录 1、用户信息 2、用户管理 (1) 创建用户 (2) 删除用户 (3) 修改用户密码 3、用户权限管理 (1) 赋予权限(grant&#xff…

Mysql5.7+Orch+proxysql+keepalive

架构设计 通过gtid配置MySQL主从,通过orch实现高可用,orch通过raft实现自身的高可用,通过proxysql实现读写分离,proxysql可自身可以配置集群,通过keepalive实现虚拟IP漂移,keepalive可以自身配置集群 配…

蓝桥杯嵌入式LED流马灯(使用HAL_Delay、操作寄存器、使用定时器)

1.STM32CubeMX的配置 将PC8-PC15设置为GPIO_Output模式,将PD2也设置为GPIO_Output模式。 通过原理图知,LED(PC8-PC15)低电平有效,锁存器(PD2)高电平有效。 初始化时我将LED设置为高电平,熄灭状态&#xff0…

MyBatis - 09 - 自定义映射resultMap

文章目录1 准备工作1.1 建表1.2 创建实体类1.3 引出一个问题方案1方案2方案32.完整代码项目结构EmpMapper接口Emp类SqlSessionUtils工具类EmpMapper.xmljdbc.propertieslog4j.xmlmybatis-config.xmlResultMapTest完整代码在后面 1 准备工作 1.1 建表 t_emp 添加测试数据&…

AOP在PowerJob中的使用,缓存锁保证并发安全,知识细节全总结

这是一篇简简单单的文章,需要你简简单单看一眼就好,如果有不明白的地方,欢迎留言讨论。 在之前的文章中出现过一次AOP的使用,就是在运行任务之前,需要判断一下,触发该任务执行的server,是不是数…

[神经网络]图神经网络(GNN)

一、概述 1.图 图用来表示一些实体(entities)之间的关系(实体表示为点(node),关系表示为边(edge))。 关系分为有方向和无方向 2.数据的图表示 以图像文件为例,我们可以用邻接矩阵来表示一张图像。每个点表示一个像素点,若一个像素点有x个相邻…

重生之我是SVG(1)-入门

概述 引用一句来自MDN的一句话: SVG 图像是使用各种元素创建的,这些元素分别应用于矢量图像的结构、绘制与布局。在这里,您可以找到每个 SVG 元素的参考文档。 SVG 文件可以直接插入网页,成为 DOM 的一部分,然后用 Ja…

华三OSPF多区域互访实验

OSPF 实验 实验拓扑 实验需求 按照图示配置 IP 地址按照图示分区域配置 OSPF ,实现全网互通为了路由结构稳定,要求路由器使用环回口作为 Router-id,ABR 的环回口宣告进骨干区域 实验解法 1.配置 IP 地址部分 2.按照图示分区域配置 OS…

Zeppelin-0.10.0的安装

目录 1.解压到指定目录 2.修改文件名 3.拷贝配置文件 4.修改IP和端口号,也可以改为8090等端口号 5.修改zeppelin-env.sh文件 6.复制hive-site.xml文件到当前目录下 7.切换目录 8.拷贝hadoop和hive的各种jar包到/opt/soft/zeppelin/interpreter/jdbc目录下 …

SDYY大学普通话考试报名系统说明文档

系列文章目录 健康云平台开发说明文档SD申报系统迭代说明文档漏刻有时物联网传感器API接口对接说明文档Echarts数据分析系统Data Analysis Platform使用说明文档漏刻有时云守护数据可视化v2.0迭代升级说明文档百度地图POI多信息点标注开发说明文档漏刻有时云守护数据可视化画质…

Docker之路(7.DockerFile文件编写、DockerFile 指令解释、CMD与ENTRYPOINT的区别)

1.DockerFile介绍 dockerfile 是用来构建docker镜像的文件!命令参数脚本! 构建步骤: 编写一个dockerfile文件docker build构建成为一个镜像docker run 运行镜像docker push发布镜像(DockerHub、阿里云镜像仓库) 2.Dock…

如何使用ADFSRelay分析和研究针对ADFS的NTLM中继攻击

关于ADFSRelay ADFSRelay是一款功能强大的概念验证工具,可以帮助广大研究人员分析和研究针对ADFS的NTLM中继攻击。 ADFSRelay这款工具由NTLMParse和ADFSRelay这两个实用程序组成。其中,NTLMParse用于解码base64编码的NTLM消息,并打印有关消…