2024年8月

1.快速幂模板

前置知识

一个数字n,它的二进制位数一定是log
2
n向下取整+1;

快速幂模板代码

这段代码实现了快速幂算法(Exponentiation by squaring),用来计算 ( a
n
) 的值,其中 ( a ) 和 ( n ) 都是整数。

int quickpow(int a, int n)
{
    int res = 1;  // 初始化结果为1,因为任何数的0次幂都是1

    while (n) {   // 当指数n不为0时,继续执行循环
        if (n & 1)  // 如果n的最低位为1(即n是奇数)
            res = res * a;  // 将当前底数a乘到结果中
        a = a * a;   // 将底数a平方,相当于底数翻倍,指数减半
        n >>= 1;     // 将指数n右移一位,相当于将指数减半
    }

    return res;  // 返回计算结果
}

现在逐句解析每一行代码的作用:

  1. int res = 1;


    • 初始化变量
      res
      为1,这是最终结果的初始值。任何数的0次幂都是1。
  2. while (n) {


    • 进入一个循环,条件是当指数
      n
      不为0时继续执行。循环将持续执行直到
      n
      变为0。
  3. if (n & 1)


    • 判断当前的指数
      n
      是否为奇数,使用位运算
      n & 1
      来判断。如果
      n
      的最低位(即最右边的二进制位)为1,则说明
      n
      是奇数。
  4. res = res * a;


    • 如果
      n
      是奇数,则将当前的底数
      a
      乘到结果
      res
      中。这步实现了快速幂算法中的乘法操作。
  5. a = a * a;


    • 然后将底数
      a
      自乘,即
      a
      变成
      a^2
      。这一步相当于将底数翻倍,对应于指数减半的操作。
  6. n >>= 1;


    • 将指数
      n
      右移一位,即
      n
      变成
      n / 2
      。这一步实现了快速幂算法中的指数减半操作。
  7. 循环回到第2步,直到
    n
    变为0,退出循环。

  8. return res;


    • 返回最终计算得到的结果
      res
      ,即底数
      a
      的指数
      n
      次幂的值。

这段代码利用了快速幂算法的思想,通过迭代和位运算的方式,将指数的计算复杂度从 ( O(n) ) 优化到 ( O(log n) ),显著提高了计算效率。

快速幂算法的形象解释

快速幂算法的例题

【模板】快速幂

题目描述

给你三个整数
\(a,b,p\)
,求
\(a^b \bmod p\)

输入格式

输入只有一行三个整数,分别代表
\(a,b,p\)

输出格式

输出一行一个字符串
a^b mod p=s
,其中
\(a,b,p\)
分别为题目给定的值,
\(s\)
为运算结果。

样例 #1

样例输入 #1

2 10 9

样例输出 #1

2^10 mod 9=7

提示

样例解释

\(2^{10} = 1024\)

\(1024 \bmod 9 = 7\)

数据规模与约定

对于
\(100\%\)
的数据,保证
\(0\le a,b < 2^{31}\)

\(a+b>0\)

\(2 \leq p \lt 2^{31}\)

答案

这题直接套用快速幂算法的模板,只需要每一步我们加上取模运算即可,注意数据需要开long long类型

#include<iostream>
using namespace std;
long long  quickpow(long long  a, long long  n,long long p)
{
	long long res = 1;
	while (n) {
		if (n & 1) res = (res * a)%p;
		a = (a * a)%p;
		n >>= 1;
	}
	return res;
}
int main()
{
	long long  a, b, p;
	cin >> a >> b >> p;
	printf("%lld^%lld mod %lld=%lld", a, b, p, quickpow(a, b, p));
	return 0;
}

详解边缘检测示例

卷积运算是卷积神经网络最基本的组成部分,使用边缘检测作为入门样例。在这个博客中,会看到卷积是如何进行运算的。

在之前的博客中,说过神经网络的前几层是如何检测边缘的,然后,后面的层有可能检测到物体的部分区域,更靠后的一些层可能检测到完整的物体,这个例子中就是人脸。在这个博客中,会看到如何在一张图片中进行边缘检测。

让举个例子,给了这样一张图片,让电脑去搞清楚这张照片里有什么物体,可能做的第一件事是检测图片中的垂直边缘。比如说,在这张图片中的栏杆就对应垂直线,与此同时,这些行人的轮廓线某种程度上也是垂线,这些线是垂直边缘检测器的输出。同样,可能也想检测水平边缘,比如说这些栏杆就是很明显的水平线,它们也能被检测到,结果在这。所以如何在图像中检测这些边缘?

看一个例子,这是一个6×6的灰度图像。因为是灰度图像,所以它是6×6×1的矩阵,而不是6×6×3的,因为没有
RGB
三通道。为了检测图像中的垂直边缘,可以构造一个3×3矩阵。在共用习惯中,在卷积神经网络的术语中,它被称为过滤器。要构造一个3×3的过滤器,像这样
\(\begin{bmatrix}1 & 0 & -1\\ 1 & 0 & -1\\ 1 & 0 & -1\end{bmatrix}\)
。在论文它有时候会被称为核,而不是过滤器,但在这个博客中,将使用过滤器这个术语。对这个6×6的图像进行卷积运算,卷积运算用“
\(*\)
”来表示,用3×3的过滤器对其进行卷积。

关于符号表示,有一些问题,在数学中“
\(*\)
”就是卷积的标准标志,但是在
Python
中,这个标识常常被用来表示乘法或者元素乘法。所以这个“
\(*\)
”有多层含义,它是一个重载符号,在这个博客中,当“
\(*\)
”表示卷积的时候会特别说明。

这个卷积运算的输出将会是一个4×4的矩阵,可以将它看成一个4×4的图像。下面来说明是如何计算得到这个4×4矩阵的。为了计算第一个元素,在4×4左上角的那个元素,使用3×3的过滤器,将其覆盖在输入图像,如下图所示。然后进行元素乘法(
element-wise products
)运算,所以
\(\begin{bmatrix} 3 \times 1 & 0 \times 0 & 1 \times \left(1 \right) \\ 1 \times 1 & 5 \times 0 & 8 \times \left( - 1 \right) \\ 2 \times1 & 7 \times 0 & 2 \times \left( - 1 \right) \\ \end{bmatrix} = \begin{bmatrix}3 & 0 & - 1 \\ 1 & 0 & - 8 \\ 2 & 0 & - 2 \\\end{bmatrix}\)
,然后将该矩阵每个元素相加得到最左上角的元素,即
\(3+1+2+0+0 +0+(-1)+(-8) +(-2)=-5\)

把这9个数加起来得到-5,当然,可以把这9个数按任何顺序相加,只是先写了第一列,然后第二列,第三列。

接下来,为了弄明白第二个元素是什么,要把蓝色的方块,向右移动一步,像这样,把这些绿色的标记去掉:

继续做同样的元素乘法,然后加起来,所以是 $0×1+5×1+7×1+1×0+8×0+2×0+2×(-1)+ 9×(-1)+5×(-1)=-4 $。

接下来也是一样,继续右移一步,把9个数的点积加起来得到0。

继续移得到8,验证一下:
\(2×1+9×1+5×1+7×0+3×0+1×0+4×(-1)+ 1×(-1)+ 3×(-1)=8\)

接下来为了得到下一行的元素,现在把蓝色块下移,现在蓝色块在这个位置:

重复进行元素乘法,然后加起来。通过这样做得到-10。再将其右移得到-2,接着是2,3。以此类推,这样计算完矩阵中的其他元素。

为了说得更清楚一点,这个-16是通过底部右下角的3×3区域得到的。

因此6×6矩阵和3×3矩阵进行卷积运算得到4×4矩阵。这些图片和过滤器是不同维度的矩阵,但左边矩阵容易被理解为一张图片,中间的这个被理解为过滤器,右边的图片可以理解为另一张图片。这个就是垂直边缘检测器。

在往下讲之前,多说一句,如果要使用编程语言实现这个运算,不同的编程语言有不同的函数,而不是用“
\(*\)
”来表示卷积。所以在编程练习中,会使用一个叫
conv_forward
的函数。如果在
tensorflow
下,这个函数叫
tf.conv2d
。在其他深度学习框架中,在后面的博客中,将会看到
Keras
这个框架,在这个框架下用
Conv2D
实现卷积运算。所有的编程框架都有一些函数来实现卷积运算。

为什么这个可以做垂直边缘检测呢?让来看另外一个例子。为了讲清楚,会用一个简单的例子。这是一个简单的6×6图像,左边的一半是10,右边一般是0。如果把它当成一个图片,左边那部分看起来是白色的,像素值10是比较亮的像素值,右边像素值比较暗,使用灰色来表示0,尽管它也可以被画成黑的。图片里,有一个特别明显的垂直边缘在图像中间,这条垂直线是从黑到白的过渡线,或者从白色到深色。

所以,当用一个3×3过滤器进行卷积运算的时候,这个3×3的过滤器可视化为下面这个样子,在左边有明亮的像素,然后有一个过渡,0在中间,然后右边是深色的。卷积运算后,得到的是右边的矩阵。如果愿意,可以通过数学运算去验证。举例来说,最左上角的元素0,就是由这个3×3块(绿色方框标记)经过元素乘积运算再求和得到的,
\(10×1+10×1+10×1+10×0+10×0+10×0+10×(-1)+10×(-1)+10×(-1)=0\)

。相反这个30是由这个(红色方框标记)得到的,

\(10×1+10×1+10×1+10×0+10×0+10×0+0×(-1)+0×(-1)+ 0×(-1)=30\)

如果把最右边的矩阵当成图像,它是这个样子。在中间有段亮一点的区域,对应检查到这个6×6图像中间的垂直边缘。这里的维数似乎有点不正确,检测到的边缘太粗了。因为在这个例子中,图片太小了。如果用一个1000×1000的图像,而不是6×6的图片,会发现其会很好地检测出图像中的垂直边缘。在这个例子中,在输出图像中间的亮处,表示在图像中间有一个特别明显的垂直边缘。从垂直边缘检测中可以得到的启发是,因为使用3×3的矩阵(过滤器),所以垂直边缘是一个3×3的区域,左边是明亮的像素,中间的并不需要考虑,右边是深色像素。在这个6×6图像的中间部分,明亮的像素在左边,深色的像素在右边,就被视为一个垂直边缘,卷积运算提供了一个方便的方法来发现图像中的垂直边缘。

〇、前言

相较于支付宝,微信支付对 .Net 的支持就没那么充分,官方没有提供 SDK。

但值得庆幸的是,在社区有大佬封装了 v3 版 .Net SDK。

原文链接:
https://developers.weixin.qq.com/community/develop/article/doc/00020aadc384a0a5f01c3526b56813

SDK 名称:SKIT.FlurlHttpClient.Wechat.TenpayV3,支持 .NET Core / Framework,完整封装全部 v3 API。

本文也将以此 SDK 来应用,主要介绍退款和退款状态查询两个接口。

一、接入准备

1.1 引入 SDK

名称:SKIT.FlurlHttpClient.Wechat.TenpayV3,支持 .Net Standard 2.0、.Net Framework 4.6.2-4.7.1、.Net 6.0 等版本。

NuGet 简介地址: https://www.nuget.org/packages/SKIT.FlurlHttpClient.Wechat.TenpayV3

简介:基于 Flurl.Http 的微信支付 API v3 版客户端,支持商户(直连)、合作伙伴(服务商、渠道商、机构、银行)模式,支持基础支付、代金券、商家券、委托营销、消费卡、支付有礼、银行定向促活、微信支付分、微信先享卡、支付即服务、点金计划、智慧商圈、电商收付通、平台收付通、二级商户进件、小微商户进件、消费者投诉、商户风控管理、商户违规通知、批量转账到零钱、银行组件、海关报关、融合钱包、微工卡、电子发票、电子小票、车主平台、教育续费通、校园轻松付等功能。

1.2 必要的账户信息

名称 示例 简介
商户号 110 ... ... 10 位,数字
商户 API 证书序列号 D566D2 ... ... 40 位,大写字母、数字,组合
商户 API v3 密钥 gSk0EA ... ... 32 位,数字、大写字母、小写字母,组合
商户 API 证书私钥 -----BEGIN PRIVATE KEY-----\r\nMIIEv ... ... 通常为 `apiclient_key.pem` 文件内容

关于微信的服务商和普通商户:

普通商户

资金由微信直接与商户结算,不涉及资金冻结和分账;
可独立申请,可自行通过接口进行支付、退款等操作;
适合大型企业或品牌商户,有自己技术团队和支付系统。

服务商

资金会先被微信冻结,服务商发起分账后微信才进行分账;
可免费申请不限个数的子商户,但服务商的商户号需要每年支付认证费用;
自身不能直接收款,需通过申请的“特约商户”才能收款;
“特约商户”可以像普通商户一样收款,但退款接口必须由服务商发起;
适合于提供支付解决方案给多个特约商户的第三方机构,特别是那些需要处理多商户分账或收取服务费的复杂场景。

特别注意:特约商户,也就是服务商下边的子商户,是没有权限直接调用退款接口的,只能根据服务商的账户信息加上子商户号来。

二、接口调用

2.1 根据账户信息创建共用客户端对象 client

var manager = new InMemoryCertificateManager();
var options = new WechatTenpayClientOptions()
{
    MerchantId = "商户号", // 商户号
    MerchantV3Secret = "商户 API v3 密钥", // 商户 API v3 密钥
    MerchantCertificateSerialNumber = "商户 API 证书序列号", // 商户 API 证书序列号
    MerchantCertificatePrivateKey = "-----BEGIN PRIVATE KEY-----\r\nMIIEv ... ... Q71AG\r\n-----END PRIVATE KEY-----", // 商户 API 证书私钥
    PlatformCertificateManager = manager
};
var client = new WechatTenpayClient(options);

2.2 退款接口

以下是退款接口的代码,其中入参
仅示例了必要的字段
,其他详情见官方文档。

官方文档: https://pay.weixin.qq.com/docs/merchant/apis/refund/refunds/create.html

var request = new CreateRefundDomesticRefundRequest()
{
    //OutTradeNumber = "商户订单号", // 【商户订单号】 原支付交易对应的商户订单号,与transaction_id二选一
    TransactionId = "微信订单号", // 【微信支付订单号】 原支付交易对应的微信订单号,与out_trade_no二选一
    // 商户自定义退款唯一标识,要做好记录,退款状态查询时入参必填
    OutRefundNumber = $"WX{DateTime.Now.ToString("yyyyMMddHHmmssffffff")}", 
    Reason = "测试退款080611",
    //NotifyUrl = "https://...", // 回调地址
    Amount = new CreateRefundDomesticRefundRequest.Types.Amount()
    {
        Total = 100, // 单位:分
        Refund = 99, // 单位:分
        Currency = "CNY" // 【退款币种】 目前只支持人民币:CNY。
    },
};
//string json = Json_Object.ObjectToJsonstr(request);
var response = await client.ExecuteCreateRefundDomesticRefundAsync(request);
if (response.IsSuccessful())
{
    Console.WriteLine("RefundId:", response.RefundId); // 【微信支付退款号】 微信支付退款号
    Console.WriteLine("OutRefundNumber:", response.OutRefundNumber); // 【商户退款单号】 商户系统内部的退款单号,商户系统内部唯一
    Console.WriteLine("TransactionId:", response.TransactionId); // 【微信支付订单号】
    Console.WriteLine("OutRefundNumber:", response.OutTradeNumber); // 【商户订单号】 原支付交易对应的商户订单号
}
else
{
    Console.WriteLine("HTTP 状态:" + response.GetRawStatus());
    Console.WriteLine("错误代码:" + response.ErrorCode);
    Console.WriteLine("错误描述:" + response.ErrorMessage);
}

2.3 退款状态查询接口

以下是退款状态查询接口的代码,入参实际上就只有一个:商户退款单号。文档中是要求直接跟在接口请求路径之后,SDK 中是封装到 Request 模型中。

官方文档: https://pay.weixin.qq.com/docs/merchant/apis/refund/refunds/query-by-out-refund-no.html

var request = new GetRefundDomesticRefundByOutRefundNumberRequest()
{
    OutRefundNumber = "WX20240806104450972506",
};
//string json = Json_Object.ObjectToJsonstr(request);
var response = await client.ExecuteGetRefundDomesticRefundByOutRefundNumberAsync(request);
if (response.IsSuccessful())
{
    Console.WriteLine("RefundId:", response.RefundId); // 【微信支付退款号】 微信支付退款号
    Console.WriteLine("OutRefundNumber:", response.OutRefundNumber); // 【商户退款单号】 商户系统内部的退款单号,商户系统内部唯一
    Console.WriteLine("OutRefundNumber:", response.TransactionId); // 【微信支付订单号】
    Console.WriteLine("OutRefundNumber:", response.OutTradeNumber); // 【商户订单号】 原支付交易对应的商户订单号
}
else
{
    Console.WriteLine("HTTP 状态:" + response.GetRawStatus());
    Console.WriteLine("错误代码:" + response.ErrorCode);
    Console.WriteLine("错误描述:" + response.ErrorMessage);
}

2.4 接口的 Request、Response 模型的对应逻辑

下面列一下以上使用到的两个接口信息:

接口名称 接口地址 Request Response
退款 【POST】/v3/refund/domestic/refunds CreateRefundDomesticRefundRequest CreateRefundDomesticRefundResponse
退款状态查询 【GET】/v3/refund/domestic/refunds/{out_refund_no} GetRefundDomesticRefundByOutRefundNumberRequest GetRefundDomesticRefundByOutRefundNumberResponse

由上表对照可以看出,对应的模型实际上就是以接口地址为基础的,其他接口就可以按照这个逻辑去一一对应。

argo-workflow-logol

本文主要记录了如何在 k8s 上快速部署云原生的工作流引擎 ArgoWorkflow。

ArgoWorkflow 是什么

Argo Workflows
是一个开源的
云原生工作流引擎
,用于在 Kubernetes 上编排并行作业。Argo 工作流作为Kubernetes CRD 实现。

  • 定义工作流,其中工作流中的每个步骤都是一个容器。
  • 将多步骤工作流建模为一系列任务,或使用 DAG 来捕获任务之间的依赖关系图。
  • 使用 Argo 可以在很短的时间内在 Kubernetes 上轻松运行机器学习或数据处理的计算密集型作业

一句话描述:
ArgoWorkflow 是一个用于在 Kubernetes 上编排并行作业的开源云原生工作流引擎

组件

相对于 Tekton 来说,ArgoWorkflow 组件比较少,整体架构比较简单。

核心组件:

  • argo-server:为工作流提供 API 和 UI 界面。
  • workflow-controller
    :真正干活的组件,解析用户创建的 CR 对象并启动 Pod 来真正运行流水线

部署

官方提供
helm chart
可以一键部署,完整命令如下:

# 添加参考并更新
helm repo add argo https://argoproj.github.io/argo-helm
helm repo update

# 部署
# -set server.authMode=server 配置切换认证模式,便于免token 登录 UI 界面
helm install argo-workflows argo/argo-workflows -n argo --create-namespace --set server.authMode=server

部署完成后会启动两个 Pod

[root@argo ~]# kubectl -n argo get po
NAME                                   READY   STATUS    RESTARTS   AGE
argo-server-84fd55bfc-hd6qp            1/1     Running   0          2m16s
workflow-controller-557756b7c8-blmp7   1/1     Running   0          2m16s

然后将 Service 切换为 NodePort 便于访问

kubectl patch svc argo-workflows-server -n argo -p '{"spec": {"type": "NodePort"}}'

最后通过 NodePort 访问即可,就像这样:
http://172.20.148.126:31691

[root@argo ~]# kubectl -n argo get svc argo-workflows-server
NAME                    TYPE       CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
argo-workflows-server   NodePort   10.108.150.173   <none>        2746:31691/TCP   2m3s

UI 界面如下,整体和 ArgoCD 挺像的:

dashboard

Demo

简单启动一个 Workflow 测试一下 ArgoWorkflow 能否正常运行。

使用以下命令创建一个 Workflow 对象:

kubectl create -f - << EOF
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
  generateName: steps-
spec:
  entrypoint: hello           # We reference our first "template" here
  templates:
  - name: hello               # The first "template" in this Workflow, it is referenced by "entrypoint"
    steps:                    # The type of this "template" is "steps"
    - - name: hello
        template: whalesay    # We reference our second "template" here
        arguments:
          parameters: [{name: message, value: "Hello ArgoWorkflow!"}]

  - name: whalesay             # The second "template" in this Workflow, it is referenced by "hello"
    inputs:
      parameters:
      - name: message
    container:                # The type of this "template" is "container"
      image: docker/whalesay
      command: [cowsay]
      args: ["{{inputs.parameters.message}}"]
EOF

功能也很简单,就是打印
Hello ArgoWorkflow!
这句话。

查看状态

[root@argo ~]# kubectl get workflow
NAME          STATUS    AGE   MESSAGE
steps-75xmq   Running   6s

可以看到,当前处于 Running 状态,不过这是一个非常简单的任务,因此很快就会结束,时间应该是耗费在拉取镜像上。

查看 Pod 运行情况

[root@argo ~]# kubectl get po -w
NAME                              READY   STATUS            RESTARTS   AGE
steps-75xmq-whalesay-1542601109   0/2     PodInitializing   0          96s

看起来正在拉取镜像,等一下

[root@argo ~]# kubectl get po
NAME                              READY   STATUS      RESTARTS   AGE
steps-75xmq-whalesay-1542601109   0/2     Completed   0          2m58s
[root@argo ~]# kubectl get workflow
NAME          STATUS      AGE    MESSAGE
steps-75xmq   Succeeded   3m6s

Pod 已经运行完成了,Workflow 也进行 Successed 状态,查看 Pod 日志,确认是否真的执行了

[root@argo ~]# kubectl logs -f steps-75xmq-whalesay-1542601109
 _____________________
< Hello ArgoWorkflow! >
 ---------------------
    \
     \
      \
                    ##        .
              ## ## ##       ==
           ## ## ## ##      ===
       /""""""""""""""""___/ ===
  ~~~ {~~ ~~~~ ~~~ ~~~~ ~~ ~ /  ===- ~~~
       \______ o          __/
        \    \        __/
          \____\______/
time="2023-10-20T08:48:32.163Z" level=info msg="sub-process exited" argo=true error="<nil>"

可以看到,确实打印出了 "Hello ArgoWorkflow" 这句话,至此说明我们部署的 ArgoWorkflow 是能够正常运行的。

到这里 ArgoWorkflow 的部署就完成了,如果理解不了这个 demo 中做的事情也没关系,后续会有 ArgoWorkflow 的使用教程,敬请期待~

下期预告:
Workflow & Template 概念模型, 构建 ArgoWorkflow 流水线


【ArgoWorkflow 系列】
持续更新中,搜索公众号【
探索云原生
】订阅,阅读更多文章。


本文介绍在
Anaconda
环境中,下载并配置
Python
中机器学习、深度学习常用的新版
tensorflow
库的方法。

在之前的两篇文章
Python TensorFlow深度学习回归代码:DNNRegressor

Python TensorFlow深度神经网络回归:keras.Sequential
中,我们介绍了利用
Python
中的
tensorflow
库,实现
机器学习

深度学习
的具体思路与代码实现;然而,当初并没有具体介绍
tensorflow
库的配置方法。因此,在这篇文章中,我们就介绍一下在
Anaconda
环境中,配置
tensorflow
库的详细方法;此外,这里需要注意,在较新版本的
tensorflow
库(版本大于
1.5
,但对于
Windows
用户而言,版本还不能高于
2.10
)中,已经同时支持
CPU

GPU
训练,不需要再区分是配置
CPU
版本的库还是
GPU
版本的库了。

首先,和
Anaconda
环境配置其他库一样,我们需要打开
Anaconda Prompt
软件;如下图所示。

image

随后,将会弹出如下所示的终端窗口。

接下来,我们即可开始
tensorflow
库的配置。由于我这里希望将
tensorflow
库配置到另一个已有的
Anaconda
虚拟环境中(这个虚拟环境的名称为
py36tf

Python
版本是
3.6
的),而不是当前这个默认的
base
环境,因此需要按照文章
创建Anaconda虚拟Python环境的方法
中提到的方法,首先进入这个名称为
py36tf
的虚拟环境中,如下图所示。

如果大家需要在默认的环境中配置
tensorflow
库,直接执行接下来的操作即可;如果大家希望新建一个环境来配置
tensorflow
库,那么参考上文提及的文章
创建Anaconda虚拟Python环境的方法
,创建并进入一个新的虚拟环境,再继续执行接下来的操作即可。

接下来,继续输入如下的代码,即可立即开始配置
tensorflow
库。

pip install --upgrade tensorflow

运行上述代码后,可以看到将立即开始
tensorflow
库的配置,如下图所示。其中,由于我这里
Python
版本是
3.6
的,而不是最新的
Python
版本,因此从下图可以看到
tensorflow
库版本也并不是最新的,而是
2.6.2
版本的——当然对我而言,这也就足够了。如果大家希望用最新版本的
tensorflow
库,需要注意同时使用最新的
Python
版本。

此外,这里有必要提一句——如果我用如下所示的代码进行
tensorflow
库的配置,配置得到的
tensorflow
库则是
1.X
版本的,而不是上面我们刚刚得到的是
2.X
版本,始终无法获取最新版本的
tensorflow
库;且之后无论怎么更新
tensorflow
库,都会出现报错信息。

conda install tensorflow

例如,在我的电脑上,如果运行上述代码,则结果如下图所示。

不知道具体是哪里的问题,从上图可以看到这种方法得到的
tensorflow
库始终是
1.X
版本(例如上图中显示
tensorflow
库就是
1.2.1
版本的)。所以,如果大家需要比较新版本的
tensorflow
库,还是建议用前面提到的
pip install --upgrade tensorflow
这句代码来实现。

让我们继续回到前述
tensorflow
库配置的工作中;稍等片刻,一般情况下即可完成
tensorflow
库的配置。这里需要注意,如果此时大家出现如下图所示的报错,则说明
tensorflow
库暂时还是没有配置成功。

这种情况是由于
pip
版本不够高导致的,因此我们需要通过如下所示的代码将
pip
升级。

python -m pip install --upgrade pip

输入上述代码,如下图所示。

运行这一代码后,我们重新运行一次
pip install --upgrade tensorflow
这句代码即可。可是在我这里,重新运行这句代码后,又出现了如下图所示的问题。

通过检查,发现网络代理的问题;将代理关闭后,即可解决问题(但是很奇怪,不知道为什么刚刚没有报这个错误,重新运行这句代码后才出现这样的错误)。最终,得到结果界面如下图所示。

接下来,我们可以输入如下的代码,从而检查
tensorflow
库是否已经配置成功。

python -c "import tensorflow as tf;print(tf.reduce_sum(tf.random.normal([1000, 1000])))"

如下图所示,如果最终得到了一个
tf.Tensor
结果,即可说明我们的
tensorflow
库终于配置完毕了。

至此,大功告成。当然,到这里或许也不算完全成功——从上图可以看到,当前
tensorflow
库并没有进行
GPU
计算。如果大家的电脑上没有
GPU
,或者不需要用
GPU
加以计算,那就不用管这个问题,相当于已经完全成功了,后续直接开始用
tensorflow
库进行各类深度学习的应用即可;但是对于电脑上有
GPU
,并且也希望让
GPU
加入计算的用户而言,我们将在下一篇博客中介绍具体的配置方法。