我试图使用*TensorFlow 2.0 beta*
在MNIST数据集上编写一个具有两个隐藏层的基本神经网络的自定义实现,但是我不确定这里出了什么问题,但是我的训练损失和准确性似乎分别停留在1.5和85左右。但是,如果我使用Keras进行构建,则仅8-10个历元就可以得到非常低的训练损失和95%以上的准确性。
我相信也许我没有更新体重或其他信息?那么,我需要将在反向传播函数中计算出的新权重分配给它们各自的权重/偏差变量吗?
如果有人可以帮助我解决我以及下面我提到的其他几个问题,我非常感谢。
几个问题:
1)如何在此自定义实现中添加一个Dropout和Batch Normalization层? (即使其在培训和测试时间内均有效)
2)如何在此代码中使用回调?即(使用EarlyStopping和ModelCheckpoint回调)
3)下面的代码中还有什么我可以进一步优化的代码,例如可以使用tensorflow 2.x @ tf.function decorator等)
4)我还需要提取获得的最终权重,以绘制和检查其分布。调查诸如梯度消失或爆炸之类的问题。 (例如:也许是Tensorboard)
5)我还需要帮助以更通用的方式编写此代码,以便我可以轻松地基于此代码实现其他网络,例如ConvNets(即Conv,MaxPool等)。
这是我易于复制的完整代码:
注意:我知道我可以使用Keras之类的高级API来轻松构建模型,但这不是我的目标。请理解。
import numpy as np
import os
import logging
logging.getLogger('tensorflow').setLevel(logging.ERROR)
import tensorflow as tf
import tensorflow_datasets as tfds
(x_train, y_train), (x_test, y_test) = tfds.load('mnist', split=['train', 'test'],
batch_size=-1, as_supervised=True)
# reshaping
x_train = tf.reshape(x_train, shape=(x_train.shape[0], 784))
x_test = tf.reshape(x_test, shape=(x_test.shape[0], 784))
ds_train = tf.data.Dataset.from_tensor_slices((x_train, y_train))
# rescaling
ds_train = ds_train.map(lambda x, y: (tf.cast(x, tf.float32)/255.0, y))
class Model(object):
def __init__(self, hidden1_size, hidden2_size, device=None):
# layer sizes along with input and output
self.input_size, self.output_size, self.device = 784, 10, device
self.hidden1_size, self.hidden2_size = hidden1_size, hidden2_size
self.lr_rate = 1e-03
# weights initializationg
self.glorot_init = tf.initializers.glorot_uniform(seed=42)
# weights b/w input to hidden1 --> 1
self.w_h1 = tf.Variable(self.glorot_init((self.input_size, self.hidden1_size)))
# weights b/w hidden1 to hidden2 ---> 2
self.w_h2 = tf.Variable(self.glorot_init((self.hidden1_size, self.hidden2_size)))
# weights b/w hidden2 to output ---> 3
self.w_out = tf.Variable(self.glorot_init((self.hidden2_size, self.output_size)))
# bias initialization
self.b1 = tf.Variable(self.glorot_init((self.hidden1_size,)))
self.b2 = tf.Variable(self.glorot_init((self.hidden2_size,)))
self.b_out = tf.Variable(self.glorot_init((self.output_size,)))
self.variables = [self.w_h1, self.b1, self.w_h2, self.b2, self.w_out, self.b_out]
def feed_forward(self, x):
if self.device is not None:
with tf.device('gpu:0' if self.device=='gpu' else 'cpu'):
# layer1
self.layer1 = tf.nn.sigmoid(tf.add(tf.matmul(x, self.w_h1), self.b1))
# layer2
self.layer2 = tf.nn.sigmoid(tf.add(tf.matmul(self.layer1,
self.w_h2), self.b2))
# output layer
self.output = tf.nn.softmax(tf.add(tf.matmul(self.layer2,
self.w_out), self.b_out))
return self.output
def loss_fn(self, y_pred, y_true):
self.loss = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y_true,
logits=y_pred)
return tf.reduce_mean(self.loss)
def acc_fn(self, y_pred, y_true):
y_pred = tf.cast(tf.argmax(y_pred, axis=1), tf.int32)
y_true = tf.cast(y_true, tf.int32)
predictions = tf.cast(tf.equal(y_true, y_pred), tf.float32)
return tf.reduce_mean(predictions)
def backward_prop(self, batch_xs, batch_ys):
optimizer = tf.keras.optimizers.Adam(learning_rate=self.lr_rate)
with tf.GradientTape() as tape:
predicted = self.feed_forward(batch_xs)
step_loss = self.loss_fn(predicted, batch_ys)
grads = tape.gradient(step_loss, self.variables)
optimizer.apply_gradients(zip(grads, self.variables))
n_shape = x_train.shape[0]
epochs = 20
batch_size = 128
ds_train = ds_train.repeat().shuffle(n_shape).batch(batch_size).prefetch(batch_size)
neural_net = Model(512, 256, 'gpu')
for epoch in range(epochs):
no_steps = n_shape//batch_size
avg_loss = 0.
avg_acc = 0.
for (batch_xs, batch_ys) in ds_train.take(no_steps):
preds = neural_net.feed_forward(batch_xs)
avg_loss += float(neural_net.loss_fn(preds, batch_ys)/no_steps)
avg_acc += float(neural_net.acc_fn(preds, batch_ys) /no_steps)
neural_net.backward_prop(batch_xs, batch_ys)
print(f'Epoch: {epoch}, Training Loss: {avg_loss}, Training ACC: {avg_acc}')
# output for 10 epochs:
Epoch: 0, Training Loss: 1.7005115111824125, Training ACC: 0.7603832868262543
Epoch: 1, Training Loss: 1.6052448933478445, Training ACC: 0.8524806404020637
Epoch: 2, Training Loss: 1.5905528008006513, Training ACC: 0.8664196092868224
Epoch: 3, Training Loss: 1.584107405738905, Training ACC: 0.8727630912326276
Epoch: 4, Training Loss: 1.5792385798413306, Training ACC: 0.8773203844903037
Epoch: 5, Training Loss: 1.5759121985174716, Training ACC: 0.8804754322627559
Epoch: 6, Training Loss: 1.5739163148682564, Training ACC: 0.8826455712551251
Epoch: 7, Training Loss: 1.5722616605926305, Training ACC: 0.8840812018606812
Epoch: 8, Training Loss: 1.569699136307463, Training ACC: 0.8867688354803249
Epoch: 9, Training Loss: 1.5679460542742163, Training ACC: 0.8885049475356936
python大神给出的解决方案
我想知道从哪里开始您的多问题,我决定从下面开始声明:
您的代码绝对不应该那样,并且与当前的Tensorflow最佳实践相差甚远。
抱歉,但是一步一步地调试它浪费了每个人的时间,不会使我们俩受益。
现在,转到第三点:
3)下面的代码中还有其他可以进一步优化的内容
在这段代码中,例如可能使用tensorflow 2.x @ tf.function
装饰等)
是的,您可以使用tensorflow2.0
功能,而您似乎正在远离这些功能(tf.function
装饰器在这里实际上没有用,暂时不要使用)。
遵循新准则也可以减轻您的第5点问题,即:
5)我也希望以更通用的方式帮助编写此代码,因此
我可以轻松实现其他网络,例如ConvNets(即Conv,MaxPool
等)基于此代码。
因为它是专门为此设计的。稍作介绍后,我将尝试通过几个步骤向您介绍这些概念:
1.将程序分为逻辑部分
Tensorflow在代码可读性方面造成了很大的伤害; tf1.x
中的所有内容通常都放在一个地方,全局变量后跟函数定义,然后是另一个全局变量,或者也许是数据加载,所有这些都是一团糟。这并不是开发人员的错,因为系统的设计鼓励了这些行为。
现在,在tf2.0
中,鼓励程序员按照类似于在pytorch
,chainer
和其他更用户友好的框架中看到的结构来划分工作。
1.1数据加载
您使用Tensorflow Datasets的路况不错,但没有明显的理由转身离开。
这是带有注释的代码,发生了什么:
# You already have tf.data.Dataset objects after load
(x_train, y_train), (x_test, y_test) = tfds.load('mnist', split=['train', 'test'],
batch_size=-1, as_supervised=True)
# But you are reshaping them in a strange manner...
x_train = tf.reshape(x_train, shape=(x_train.shape[0], 784))
x_test = tf.reshape(x_test, shape=(x_test.shape[0], 784))
# And building from slices...
ds_train = tf.data.Dataset.from_tensor_slices((x_train, y_train))
# Unreadable rescaling (there are built-ins for that)
您可以轻松地将这个想法推广到任何数据集,将其放在单独的模块中,例如datasets.py
:
import tensorflow as tf
import tensorflow_datasets as tfds
class ImageDatasetCreator:
@classmethod
# More portable and readable than dividing by 255
def _convert_image_dtype(cls, dataset):
return dataset.map(
lambda image, label: (
tf.image.convert_image_dtype(image, tf.float32),
label,
)
)
def __init__(self, name: str, batch: int, cache: bool = True, split=None):
# Load dataset, every dataset has default train, test split
dataset = tfds.load(name, as_supervised=True, split=split)
# Convert to float range
try:
self.train = ImageDatasetCreator._convert_image_dtype(dataset["train"])
self.test = ImageDatasetCreator._convert_image_dtype(dataset["test"])
except KeyError as exception:
raise ValueError(
f"Dataset {name} does not have train and test, write your own custom dataset handler."
) from exception
if cache:
self.train = self.train.cache() # speed things up considerably
self.test = self.test.cache()
self.batch: int = batch
def get_train(self):
return self.train.shuffle().batch(self.batch).repeat()
def get_test(self):
return self.test.batch(self.batch).repeat()
因此,现在您可以使用简单的命令加载超过mnist
的内容:
from datasets import ImageDatasetCreator
if __name__ == "__main__":
dataloader = ImageDatasetCreator("mnist", batch=64, cache = True)
train, test = dataloader.get_train(), dataloader.get_test()
从现在开始,您可以使用除mnist
之外的任何其他名称来加载数据集。
请停止编写与深度学习相关的所有内容,并同时编写脚本,您也是程序员。
1.2模型创建
由于tf2.0
有两种建议的方法,一种可以根据模型的复杂性进行:
tensorflow.keras.models.Sequential
-@Stewart_R演示了这种方式,无需重复他的观点。用于最简单的模型(您应该将此模型与前馈一起使用)。
继承tensorflow.keras.Model
并编写自定义模型。当模块中有某种逻辑或更复杂(例如ResNets,多路径网络等)时,应使用此逻辑。总而言之,更具可读性和可定制性。
您的Model
班级试着像这样,但它又向南走了。 backprop
绝对不是模型本身的一部分,也不是loss
或accuracy
,请将它们分为另一个模块或函数,而不是成员!
就是说,让我们使用第二种方法对网络进行编码(为简便起见,应将此代码放在model.py
中)。在此之前,我将通过继承YourDense
从头开始编写tf.keras.Layers
前馈层(这可能会进入layers.py
模块):
import tensorflow as tf
class YourDense(tf.keras.layers.Layer):
def __init__(self, units):
# It's Python 3, you don't have to specify super parents explicitly
super().__init__()
self.units = units
# Use build to create variables, as shape can be inferred from previous layers
# If you were to create layers in __init__, one would have to provide input_shape
# (same as it occurs in PyTorch for example)
def build(self, input_shape):
# You could use different initializers here as well
self.kernel = self.add_weight(
shape=(input_shape[-1], self.units),
initializer="random_normal",
trainable=True,
)
# You could define bias in __init__ as well as it's not input dependent
self.bias = self.add_weight(shape=(self.units,), initializer="random_normal")
# Oh, trainable=True is default
def call(self, inputs):
# Use overloaded operators instead of tf.add, better readability
return tf.matmul(inputs, self.kernel) + self.bias
关于你
1)如何在此自定义中添加一个Dropout和Batch Normalization图层
实施? (即使其在培训和测试时间内均有效)
我想您想为这些层创建一个自定义实现。
如果没有,您可以导入from tensorflow.keras.layers import Dropout
并在@Leevo指出的任何位置使用它。
在以下train
和test
期间具有不同行为的倒置辍学:
class CustomDropout(layers.Layer):
def __init__(self, rate, **kwargs):
super().__init__(**kwargs)
self.rate = rate
def call(self, inputs, training=None):
if training:
# You could simply create binary mask and multiply here
return tf.nn.dropout(inputs, rate=self.rate)
# You would need to multiply by dropout rate if you were to do that
return self.rate * inputs
from here并修改图层以更好地适应展示目的。
现在,您终于可以创建模型了(简单的双前馈):
import tensorflow as tf
from layers import YourDense
class Model(tf.keras.Model):
def __init__(self):
super().__init__()
# Use Sequential here for readability
self.network = tf.keras.Sequential(
[YourDense(100), tf.keras.layers.ReLU(), YourDense(10)]
)
def call(self, inputs):
# You can use non-parametric layers inside call as well
flattened = tf.keras.layers.Flatten()(inputs)
return self.network(flattened)
Ofc,在常规实现中应尽可能使用内置函数。
这种结构是相当可扩展的,因此可以泛化为卷积网络,resnets,senets,无论通过此模块执行什么操作。您可以here了解更多信息。
我认为这符合您的第5点:
5)我也希望以更通用的方式帮助编写此代码,因此
我可以轻松实现其他网络,例如ConvNets(即Conv,MaxPool
等)基于此代码。
最后一件事,您可能必须使用model.build(shape)
来构建模型图。
model.build((None, 28, 28, 1))
这将适用于MNIST的28x28x1
输入形状,其中None
代表批处理。
1.3培训
再一次,培训可以通过两种不同的方式进行:
标准Keras model.fit(dataset)
-在分类等简单任务中很有用tf.GradientTape
-更复杂的训练方案,最突出的例子是Generative Adversarial Networks,其中两个模型在玩maxmax游戏时优化正交目标
正如@Leevo再次指出的那样,如果要使用第二种方法,将无法简单地使用Keras提供的回调,因此,我建议尽可能使用第一种方法。
从理论上讲,您可以手动调用回调函数,例如on_batch_begin()
以及需要的其他函数,但这会很麻烦,而且我不确定这将如何工作。
对于第一个选项,您可以直接使用tf.data.Dataset
对象。这是另一个模块(最好是train.py
)中提供的内容:
def train(
model: tf.keras.Model,
path: str,
train: tf.data.Dataset,
epochs: int,
steps_per_epoch: int,
validation: tf.data.Dataset,
steps_per_validation: int,
stopping_epochs: int,
optimizer=tf.optimizers.Adam(),
):
model.compile(
optimizer=optimizer,
# I used logits as output from the last layer, hence this
loss=tf.losses.SparseCategoricalCrossentropy(from_logits=True),
metrics=[tf.metrics.SparseCategoricalAccuracy()],
)
model.fit(
train,
epochs=epochs,
steps_per_epoch=steps_per_epoch,
validation_data=validation,
validation_steps=steps_per_validation,
callbacks=[
# Tensorboard logging
tf.keras.callbacks.TensorBoard(
pathlib.Path("logs")
/ pathlib.Path(datetime.datetime.now().strftime("%Y%m%d-%H%M%S")),
histogram_freq=1,
),
# Early stopping with best weights preserving
tf.keras.callbacks.EarlyStopping(
monitor="val_sparse_categorical_accuracy",
patience=stopping_epochs,
restore_best_weights=True,
),
],
)
model.save(path)
更复杂的方法与PyTorch
训练循环非常相似(几乎是复制和粘贴),因此,如果您熟悉这些循环,就不会造成太大的问题。
您可以在整个tf2.0
文档中找到示例,例如here或here。
2.其他事项
2.1未回答的问题
4)代码中还有其他我可以进一步优化的地方
这个代码?即(使用tensorflow 2.x @ tf.function装饰器
等等。)
上面已经将Model转换为图形,因此在这种情况下调用它不会使您受益。过早的优化是万恶之源,请记住在执行此操作之前先对代码进行评估。
适当地缓存数据(如#1.1开头所述)和良好的管道传输将给您带来更多收益。
5)我还需要一种方法来提取所有图层的所有最终权重
经过培训后,我就可以绘制它们并检查它们的分布。至
检查诸如梯度消失或爆炸之类的问题。
正如上面的@Leevo所指出的,
weights = model.get_weights()
会帮助您减轻体重。您可以将它们转换为np.array
并使用seaborn
,matplotlib
进行绘图,分析,检查或其他所需的操作。
2.2放在一起
总而言之,您的main.py
(或入口点或类似名称)将由以下内容(或多或少)组成:
from dataset import ImageDatasetCreator
from model import Model
from train import train
# You could use argparse for things like batch, epochs etc.
if __name__ == "__main__":
dataloader = ImageDatasetCreator("mnist", batch=64, cache=True)
train, test = dataloader.get_train(), dataloader.get_test()
model = Model()
model.build((None, 28, 28, 1))
train(
model, train, path epochs, test, len(train) // batch, len(test) // batch, ...
) # provide necessary arguments appropriately
# Do whatever you want with those
weights = model.get_weights()
哦,请记住,上面的功能不是用于复制粘贴的,应该将其视为准则。如有任何疑问,请打我。
3.评论问题
3.1如何初始化自定义层和内置层
3.1.1 TLDR您将要阅读的内容
自定义Poisson初始化功能,但需要三个
论点tf.keras.initalization
API需要两个参数(请参见最后一点in their docs),因此一个是
通过Python的lambda
在我们之前编写的自定义层中指定
添加了该层的可选偏压,可以使用
布尔值
为什么如此无用的复杂?为了说明这一点,您终于可以在Python中使用Python的功能了,不再需要图形麻烦,可以使用tf2.0
代替if
等。
3.1.2从TLDR到实施
可以找到Keras初始化器here和Tensorflow的风味here。
请注意API不一致(大写字母,如类,小写字母,带下划线,如函数),尤其是在tf.cond
中,但这是重点。
您可以通过传递字符串(如上面的tf2.0
中所述)或在对象创建过程中使用它们。
为了允许在自定义层中进行自定义初始化,您可以简单地向构造函数添加其他参数(YourDense
类仍然是Python类,它的tf.keras.Model
应该与Python相同)。
在此之前,我将向您展示如何创建自定义初始化:
# Poisson custom initialization because why not.
def my_dumb_init(shape, lam, dtype=None):
return tf.squeeze(tf.random.poisson(shape, lam, dtype=dtype))
注意,它的签名带有三个参数,而只能带有__init__
。仍然可以在创建自己的图层时轻松地“修复”此问题,例如以下图层(扩展为(shape, dtype)
):
import typing
import tensorflow as tf
class YourDense(tf.keras.layers.Layer):
# It's still Python, use it as Python, that's the point of tf.2.0
@classmethod
def register_initialization(cls, initializer):
# Set defaults if init not provided by user
if initializer is None:
# let's make the signature proper for init in tf.keras
return lambda shape, dtype: my_dumb_init(shape, 1, dtype)
return initializer
def __init__(
self,
units: int,
bias: bool = True,
# can be string or callable, some typing info added as well...
kernel_initializer: typing.Union[str, typing.Callable] = None,
bias_initializer: typing.Union[str, typing.Callable] = None,
):
super().__init__()
self.units: int = units
self.kernel_initializer = YourDense.register_initialization(kernel_initializer)
if bias:
self.bias_initializer = YourDense.register_initialization(bias_initializer)
else:
self.bias_initializer = None
def build(self, input_shape):
# Simply pass your init here
self.kernel = self.add_weight(
shape=(input_shape[-1], self.units),
initializer=self.kernel_initializer,
trainable=True,
)
if self.bias_initializer is not None:
self.bias = self.add_weight(
shape=(self.units,), initializer=self.bias_initializer
)
else:
self.bias = None
def call(self, inputs):
weights = tf.matmul(inputs, self.kernel)
if self.bias is not None:
return weights + self.bias
我已经添加了YourLinear
作为默认值(如果用户未提供),并使用my_dumb_initialization
参数使bias为可选。请注意,只要不依赖数据,就可以自由使用bias
。如果是(或某种程度上依赖于if
),则必须使用tf.Tensor
装饰器,该装饰器将Python的流更改为其对应的@tf.function
(例如tensorflow
到if
)。
有关签名的更多信息,请参见here,这很容易遵循。
如果要将上述初始化程序更改合并到模型中,则必须创建适当的对象。
... # Previous of code Model here
self.network = tf.keras.Sequential(
[
YourDense(100, bias=False, kernel_initializer="lecun_uniform"),
tf.keras.layers.ReLU(),
YourDense(10, bias_initializer=tf.initializers.Ones()),
]
)
... # and the same afterwards
使用内置的tf.cond
层,可以执行相同的操作(参数名称不同,但思想保留)。
3.2使用tf.keras.layers.Dense
自动区分
3.2.1简介
tf.GradientTape
的点是允许用户对另一个变量进行正常的Python控制流和变量的梯度计算。
取自here的示例,但分为几个部分:
def f(x, y):
output = 1.0
for i in range(y):
if i > 1 and i < 5:
output = tf.multiply(output, x)
return output
具有tf.GradientTape
和for
流控制语句的常规python函数
def grad(x, y):
with tf.GradientTape() as t:
t.watch(x)
out = f(x, y)
return t.gradient(out, x)
使用渐变磁带,您可以记录if
上的所有操作(以及它们的中间状态)并向后“播放”(使用追迹规则执行自动向后微分)。
Tensors
上下文管理器中的每个Tensor
被自动记录。如果某些Tensor超出范围,请使用tf.GradientTape()
方法,如上所示。
最后,相对于watch()
的output
梯度(返回输入)。
3.2.2与深度学习的联系
上面描述的是x
算法。为网络中的每个节点(或每个层)计算(相对于)输出的梯度w.r.t。然后,各种优化程序会使用这些梯度进行校正,因此重复进行。
让我们继续,假设您已经设置了backpropagation
,优化程序实例,tf.keras.Model
和损失函数。
可以定义一个tf.data.Dataset
类,该类将为我们进行培训。如有疑问,请阅读代码中的注释:
class Trainer:
def __init__(self, model, optimizer, loss_function):
self.model = model
self.loss_function = loss_function
self.optimizer = optimizer
# You could pass custom metrics in constructor
# and adjust train_step and test_step accordingly
self.train_loss = tf.keras.metrics.Mean(name="train_loss")
self.test_loss = tf.keras.metrics.Mean(name="train_loss")
def train_step(self, x, y):
# Setup tape
with tf.GradientTape() as tape:
# Get current predictions of network
y_pred = self.model(x)
# Calculate loss generated by predictions
loss = self.loss_function(y, y_pred)
# Get gradients of loss w.r.t. EVERY trainable variable (iterable returned)
gradients = tape.gradient(loss, self.model.trainable_variables)
# Change trainable variable values according to gradient by applying optimizer policy
self.optimizer.apply_gradients(zip(gradients, self.model.trainable_variables))
# Record loss of current step
self.train_loss(loss)
def train(self, dataset):
# For N epochs iterate over dataset and perform train steps each time
for x, y in dataset:
self.train_step(x, y)
def test_step(self, x, y):
# Record test loss separately
self.test_loss(self.loss_function(y, self.model(x)))
def test(self, dataset):
# Iterate over whole dataset
for x, y in dataset:
self.test_step(x, y)
def __str__(self):
# You need Python 3.7 with f-string support
# Just return metrics
return f"Loss: {self.train_loss.result()}, Test Loss: {self.test_loss.result()}"
现在,您可以像下面这样简单地在代码中使用此类:
EPOCHS = 5
# model, optimizer, loss defined beforehand
trainer = Trainer(model, optimizer, loss)
for _ in range(EPOCHS):
trainer.train(train_dataset) # Same for training and test datasets
trainer.test(test_dataset)
print(f"Epoch {epoch}: {trainer})")
打印会告诉您每个时期的训练和测试损失。您可以根据需要将训练和测试混合使用(例如,训练5个纪元和1个测试),还可以添加其他指标等。
如果要使用非面向OOP的方法,请参见here(IMO的可读性较差,但每种方法都有它的用法)。
在matplotlib动画模块中管理动态绘图 - python我想要一个迭代绘制的图,该图允许跳到下一帧,停止它并返回到上一帧。我看过matplotlib动画模块,如果有一种方法可以实现以前的帧功能(例如,按下某个键时向后运行动画几帧),则该模块将是完美的像这样的事情会很好:def update_frame(i, data): fig.set_data(data[i]) 但是我可以明确地管理迭代器是增加还是减少。有没有…
Python sqlite3数据库已锁定 - python我在Windows上使用Python 3和sqlite3。我正在开发一个使用数据库存储联系人的小型应用程序。我注意到,如果应用程序被强制关闭(通过错误或通过任务管理器结束),则会收到sqlite3错误(sqlite3.OperationalError:数据库已锁定)。我想这是因为在应用程序关闭之前,我没有正确关闭数据库连接。我已经试过了: connectio…
使用Tensorflow 2.0-AMI设置AWS EC2实例还是自己构建它? - python我需要使用Tensorflow 2.0设置一个AWS EC2 GPU实例。我看到的所有文档都表明当前的AWS AMI映像仅支持Tensorflow 1.14或1.15,但不支持Tensorflow 2.0。因此,我想知道在AWS实例上获取Tensorflow-gpu 2.0的最佳方法是什么。我可以创建一个EC2 GPU实例,安装Nvidia驱动程序,然后使用…
Python pytz时区函数返回的时区为9分钟 - python由于某些原因,我无法从以下代码中找出原因:>>> from pytz import timezone >>> timezone('America/Chicago') 我得到:<DstTzInfo 'America/Chicago' LMT-1 day, 18:09:00 STD…
用大写字母拆分字符串,但忽略AAA Python Regex - python我的正则表达式:vendor = "MyNameIsJoe. I'mWorkerInAAAinc." ven = re.split(r'(?<=[a-z])[A-Z]|[A-Z](?=[a-z])', vendor) 以大写字母分割字符串,例如:'我的名字是乔。 I'mWorkerInAAAinc”变成…