[Web CV] Deploy MobileNet for the web

<实践复现此项目>

课程:https://www.coursera.org/learn/browser-based-models-tensorflow/home/welcome

代码:https://github.com/lmoroney/dlaicourse

参考:[MobileNet] Train MobilenNet in Keras & TF Hub

Jeff 2020: 直接跳到"项目复现"即可。

 

 

基础练习


一、the first training.  

Build a model, but not load a model, and then training.

复制代码
<html>
<head></head>
    <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@latest"></script>
    <script lang="js">
        async function doTraining(model){
            const history = 
                  await model.fit(xs, ys, 
                        { epochs: 500,
                          callbacks:{
                              onEpochEnd: async(epoch, logs) =>{
                                  console.log("Epoch:" 
                                              + epoch 
                                              + " Loss:" 
                                              + logs.loss);
                                  
                              }
                          }
                        });
        }
        const model = tf.sequential();
        model.add(tf.layers.dense({units: 1, inputShape: [1]}));
        model.compile({loss:'meanSquaredError', optimizer:'sgd'});
        model.summary();
        const xs = tf.tensor2d([-1.0, 0.0, 1.0, 2.0, 3.0, 4.0], [6, 1]);
        const ys = tf.tensor2d([-3.0, -1.0, 2.0, 3.0, 5.0, 7.0], [6, 1]);
        doTraining(model).then(() => {
            alert(model.predict(tf.tensor2d([10], [1,1])));
        });
    </script>
<body>
    <h1>First HTML Page</h1>
</body>
</html>
复制代码

 

二、训练可视化

 

三、Image Classification Using MobileNet

Download: https://github.com/lmoroney/dlaicourse/tree/master/TensorFlow%20Deployment/Course%201%20-%20TensorFlow-JS/Week%203/Examples

When you launch the mobilenet.html file in the Chrome browser make sure to open the Developer Tools to see the output in the Console.

复制代码
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@latest"> </script>
<script src="https://cdn.jsdelivr.net/npm/@tensorflow-models/mobilenet@1.0.0"> </script> 
</head>
<body>
    <img id="img" src="coffee.jpg"></img>
    <div id="output" style="font-family:courier;font-size:24px;height=300px"></div>
</body>
<script>
    const img  = document.getElementById('img');
    const outp = document.getElementById('output');
    mobilenet.load().then(model => {
        model.classify(img).then(predictions => {
            console.log(predictions);
            for(var i = 0; i<predictions.length; i++){
                outp.innerHTML += "<br/>" + predictions[i].className + " : " + predictions[i].probability;
            }
        });
    });
</script>
</html>
复制代码

 

  

 

 

自定义模型


线性模型

一、简单的线性模型

模型定义

复制代码
model = tf.keras.models.Sequential([
    # Note the input shape is the desired size of the image 150x150 with 3 bytes color
    tf.keras.layers.Conv2D(16, (3,3), activation='relu', input_shape=(150, 150, 3)),
    tf.keras.layers.MaxPooling2D(2,2),
    tf.keras.layers.Conv2D(32, (3,3), activation='relu'),
    tf.keras.layers.MaxPooling2D(2,2), 
    tf.keras.layers.Conv2D(64, (3,3), activation='relu'), 
    tf.keras.layers.MaxPooling2D(2,2),
# ---------------------------------------------------
# Flatten the results to feed into a DNN tf.keras.layers.Flatten(), # 512 neuron hidden layer tf.keras.layers.Dense(512, activation='relu'), # Only 1 output neuron. It will contain a value from 0-1 where 0 for 1 class ('cats') and 1 for the other ('dogs') tf.keras.layers.Dense(1, activation='sigmoid') ])
复制代码

确实没有MobileNet的收敛速度快.

复制代码
Train for 100 steps, validate for 50 steps
Epoch 1/15
100/100 - 19s - loss: 0.7329 - accuracy: 0.5675 - val_loss: 0.6213 - val_accuracy: 0.6590
Epoch 2/15
100/100 - 18s - loss: 0.6094 - accuracy: 0.6920 - val_loss: 0.5722 - val_accuracy: 0.7210
Epoch 3/15
100/100 - 18s - loss: 0.5236 - accuracy: 0.7470 - val_loss: 0.5856 - val_accuracy: 0.7050
Epoch 4/15
100/100 - 19s - loss: 0.4484 - accuracy: 0.7925 - val_loss: 0.7536 - val_accuracy: 0.6630
Epoch 5/15
100/100 - 20s - loss: 0.3529 - accuracy: 0.8350 - val_loss: 0.5920 - val_accuracy: 0.7170
Epoch 6/15
100/100 - 19s - loss: 0.2833 - accuracy: 0.8795 - val_loss: 0.6584 - val_accuracy: 0.7250
Epoch 7/15
100/100 - 19s - loss: 0.2161 - accuracy: 0.9155 - val_loss: 0.8035 - val_accuracy: 0.7250
Epoch 8/15
100/100 - 19s - loss: 0.1417 - accuracy: 0.9420 - val_loss: 0.9094 - val_accuracy: 0.7110
Epoch 9/15
100/100 - 19s - loss: 0.1083 - accuracy: 0.9575 - val_loss: 1.0949 - val_accuracy: 0.7290
Epoch 10/15
100/100 - 20s - loss: 0.0729 - accuracy: 0.9735 - val_loss: 1.2127 - val_accuracy: 0.7310
Epoch 11/15
100/100 - 20s - loss: 0.0610 - accuracy: 0.9785 - val_loss: 1.4155 - val_accuracy: 0.7110
Epoch 12/15
100/100 - 20s - loss: 0.0601 - accuracy: 0.9825 - val_loss: 1.0516 - val_accuracy: 0.6820
Epoch 13/15
100/100 - 20s - loss: 0.0527 - accuracy: 0.9860 - val_loss: 2.0745 - val_accuracy: 0.7280
Epoch 14/15
100/100 - 19s - loss: 0.0826 - accuracy: 0.9825 - val_loss: 1.7811 - val_accuracy: 0.7290
Epoch 15/15
100/100 - 19s - loss: 0.0669 - accuracy: 0.9855 - val_loss: 1.8485 - val_accuracy: 0.7060
training log
复制代码

 

二、模型保存

浏览器端直接下载文件.

const saveResult = model.save('downloads://model');

 

 

MobileNet模型

一、加载预训练模型

复制代码
let mobilenet;
let model;
const webcam  = new Webcam(document.getElementById('wc'));
const dataset = new RPSDataset();
var rockSamples=0, paperSamples=0, scissorsSamples=0; let isPredicting = false;
//--------------------------------------------------------
async
function loadMobilenet() { const mobilenet = await tf.loadLayersModel('https://storage.googleapis.com/tfjs-models/tfjs/mobilenet_v1_0.25_224/model.json'); const layer = mobilenet.getLayer('conv_pw_13_relu'); return tf.model({inputs: mobilenet.inputs, outputs: layer.output}); } async function init(){ await webcam.setup(); mobilenet = await loadMobilenet(); tf.tidy(() => mobilenet.predict(webcam.capture())); } init();
复制代码

 

二、准备数据

label = parseInt(elem.id);
const img = webcam.capture();
dataset.addExample(mobilenet.predict(img), label);

训练数据的保存:

复制代码
class RPSDataset {
constructor() {
this.labels = [] } addExample(example, label) { if (this.xs == null) { this.xs = tf.keep(example);  // The tensor to keep from being disposed. this.labels.push(label); } else { const oldX = this.xs; this.xs = tf.keep(oldX.concat(example, 0)); this.labels.push(label); oldX.dispose(); } }
// for training process. encodeLabels(numClasses) {
for (var i = 0; i < this.labels.length; i++) { if (this.ys == null) { this.ys = tf.keep(tf.tidy( () => {return tf.oneHot( tf.tensor1d([this.labels[i]]).toInt(), numClasses)})); } else { const y = tf.tidy( () => {return tf.oneHot( tf.tensor1d([this.labels[i]]).toInt(), numClasses)}); const oldY = this.ys; this.ys = tf.keep(oldY.concat(y, 0)); oldY.dispose(); y.dispose(); } } } }
复制代码

  

三、训练数据

复制代码
async function train() {
dataset.ys
= null; dataset.encodeLabels(3);  // 给ys赋值完毕
model
= tf.sequential({ layers: [ tf.layers.flatten({inputShape: mobilenet.outputs[0].shape.slice(1)}), tf.layers.dense({ units: 100, activation: 'relu'}), tf.layers.dense({ units: 3, activation: 'softmax'}) ] });
const optimizer
= tf.train.adam(0.0001); model.compile({optimizer: optimizer, loss: 'categoricalCrossentropy'});
let loss
= 0; model.fit(dataset.xs, dataset.ys, { epochs: 10, callbacks: { onBatchEnd: async (batch, logs) => { loss = logs.loss.toFixed(5); console.log('LOSS: ' + loss); } } }); }
复制代码

 

 

 

 

 

项目复现


一、代码下载

项目代码: Retrain MobileNet for the web

 

二、迁移学习

选择合适的版本:

AttributeError: module 'tensorflow' has no attribute 'app' #34431

 

Issue: cannot import name 'image_module_info_pb2'

Goto: Creating the TensorFlow Hub pip package using Linux

 

开始训练:

复制代码
# cd /path/to/retrain-mobilenet-v1
python -m scripts.retrain -h

# Create folders
mkdir -p tf_files/{bottlenecks,dataset,models,training_summaries}

# Add your dataset
cp -R /path/to/my-dataset/* tf_files/dataset

# Set environment variables
IMAGE_SIZE=128
ARCHITECTURE=mobilenet_0.25_$IMAGE_SIZE

# Start training
python -m scripts.retrain \
  --image_dir=tf_files/dataset \
  --model_dir=tf_files/models \
  --architecture=$ARCHITECTURE \
  --output_graph=tf_files/retrained_graph.pb \
  --output_labels=tf_files/retrained_labels.txt \
  --bottleneck_dir=tf_files/bottlenecks \
  --summaries_dir=tf_files/training_summaries/$ARCHITECTURE \
  --how_many_training_steps=400 \
  --learning_rate=0.001
复制代码

 

训练结果:

$ls tf_files/
frozen_graph.pb  labels.txt  quantized_graph.pb

 

三、测试模型

测试成功,完美。

复制代码
$ python scripts/label_image.py   --graph=tf_files/retrained_graph.pb   --input_width=$IMAGE_SIZE   --input_height=$IMAGE_SIZE --image=tf_files/dataset/positive_isDemage/3_roiImg1.jpg
WARNING:tensorflow:From scripts
/label_image.py:29: The name tf.GraphDef is deprecated. Please use tf.compat.v1.GraphDef instead. WARNING:tensorflow:From scripts/label_image.py:42: The name tf.read_file is deprecated. Please use tf.io.read_file instead. WARNING:tensorflow:From scripts/label_image.py:56: The name tf.image.resize_bilinear is deprecated. Please use tf.compat.v1.image.resize_bilinear instead. WARNING:tensorflow:From scripts/label_image.py:58: The name tf.Session is deprecated. Please use tf.compat.v1.Session instead. 2020-06-10 13:28:10.983890: I tensorflow/core/platform/cpu_feature_guard.cc:142] Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX2 FMA 2020-06-10 13:28:11.004028: I tensorflow/core/platform/profile_utils/cpu_utils.cc:94] CPU Frequency: 1999965000 Hz 2020-06-10 13:28:11.004412: I tensorflow/compiler/xla/service/service.cc:168] XLA service 0x557d967b5fe0 initialized for platform Host (this does not guarantee that XLA will be used). Devices: 2020-06-10 13:28:11.004438: I tensorflow/compiler/xla/service/service.cc:176] StreamExecutor device (0): Host, Default Version WARNING:tensorflow:From scripts/label_image.py:65: The name tf.gfile.GFile is deprecated. Please use tf.io.gfile.GFile instead. Evaluation time (1-image): 0.068s positive isdemage (score=0.50109) negative isdemage (score=0.49891)
复制代码

 

四、Optimize for the web

模型压缩

大小没什么变化;压缩后就能看到quantized的效果。

复制代码
$ python ./scripts/quantize_graph.py   --input=tf_files/retrained_graph.pb   --output=tf_files/quantized_graph.pb   --output_node_names=final_result   --mode=weights_rounded
WARNING:tensorflow:From .
/scripts/quantize_graph.py:1191: extract_sub_graph (from tensorflow.python.framework.graph_util_impl) is deprecated and will be removed in a future version. Instructions for updating: Use `tf.compat.v1.graph_util.extract_sub_graph` W0610 15:01:24.278689 139993801140032 deprecation.py:323] From ./scripts/quantize_graph.py:1191: extract_sub_graph (from tensorflow.python.framework.graph_util_impl) is deprecated and will be removed in a future version. Instructions for updating: Use `tf.compat.v1.graph_util.extract_sub_graph` WARNING:tensorflow:From ./scripts/quantize_graph.py:1294: FastGFile.__init__ (from tensorflow.python.platform.gfile) is deprecated and will be removed in a future version. Instructions for updating: Use tf.gfile.GFile. W0610 15:01:24.287163 139993801140032 deprecation.py:323] From ./scripts/quantize_graph.py:1294: FastGFile.__init__ (from tensorflow.python.platform.gfile) is deprecated and will be removed in a future version. Instructions for updating: Use tf.gfile.GFile.
jeffrey@unsw
-ThinkPad-T490:retrain-mobilenet-v1$ ls tf_files/ -l total 3948 drwxr-xr-x 4 jeffrey jeffrey 4096 Jun 10 13:15 bottlenecks drwxr-xr-x 4 jeffrey jeffrey 4096 Jun 10 13:09 dataset drwxr-xr-x 3 jeffrey jeffrey 4096 Jun 10 13:15 models -rw-r--r-- 1 jeffrey jeffrey 2008026 Jun 10 15:01 quantized_graph.pb -rw-r--r-- 1 jeffrey jeffrey 2008024 Jun 10 13:15 retrained_graph.pb -rw-r--r-- 1 jeffrey jeffrey 36 Jun 10 13:15 retrained_labels.txt drwxr-xr-x 3 jeffrey jeffrey 4096 Jun 10 13:15 training_summaries
复制代码
$ gzip -k tf_files/retrained_graph.pb tf_files/quantized_graph.pb
$ du -h tf_files/*.gz
640K    tf_files/quantized_graph.pb.gz
1.8M    tf_files/retrained_graph.pb.gz

 

Convert to TensorFlow.js model

转化为 tfjs模型。

$ tensorflowjs_converter \
   --input_format=tf_frozen_model \
   --output_node_names=final_result \
   tf_files/quantized_graph.pb \
   tf_files/web
复制代码
2020-06-10 15:19:58.882846: I tensorflow/core/grappler/optimizers/meta_optimizer.cc:786] Optimization results for grappler item: graph_to_optimize
2020-06-10 15:19:58.882905: I tensorflow/core/grappler/optimizers/meta_optimizer.cc:788]   debug_stripper: debug_stripper did nothing. time = 0.128ms.
2020-06-10 15:19:58.882913: I tensorflow/core/grappler/optimizers/meta_optimizer.cc:788]   model_pruner: Graph size after: 420 nodes (-140), 446 edges (-140), time = 4.258ms.
2020-06-10 15:19:58.882918: I tensorflow/core/grappler/optimizers/meta_optimizer.cc:788]   constant_folding: Graph size after: 176 nodes (-244), 175 edges (-271), time = 21.271ms.
2020-06-10 15:19:58.882924: I tensorflow/core/grappler/optimizers/meta_optimizer.cc:788]   arithmetic_optimizer: Graph size after: 176 nodes (0), 175 edges (0), time = 4.154ms.
2020-06-10 15:19:58.882929: I tensorflow/core/grappler/optimizers/meta_optimizer.cc:788]   dependency_optimizer: Graph size after: 174 nodes (-2), 173 edges (-2), time = 1.247ms.
2020-06-10 15:19:58.882935: I tensorflow/core/grappler/optimizers/meta_optimizer.cc:788]   model_pruner: Graph size after: 174 nodes (0), 173 edges (0), time = 0.873ms.
2020-06-10 15:19:58.882941: I tensorflow/core/grappler/optimizers/meta_optimizer.cc:788]   constant_folding: Graph size after: 174 nodes (0), 173 edges (0), time = 3.476ms.
2020-06-10 15:19:58.882947: I tensorflow/core/grappler/optimizers/meta_optimizer.cc:788]   arithmetic_optimizer: Graph size after: 174 nodes (0), 173 edges (0), time = 3.773ms.
2020-06-10 15:19:58.882953: I tensorflow/core/grappler/optimizers/meta_optimizer.cc:788]   dependency_optimizer: Graph size after: 174 nodes (0), 173 edges (0), time = 1.043ms.
2020-06-10 15:19:58.882960: I tensorflow/core/grappler/optimizers/meta_optimizer.cc:788]   debug_stripper: debug_stripper did nothing. time = 0.261ms.
2020-06-10 15:19:58.882968: I tensorflow/core/grappler/optimizers/meta_optimizer.cc:788]   model_pruner: Graph size after: 174 nodes (0), 173 edges (0), time = 0.81ms.
2020-06-10 15:19:58.882975: I tensorflow/core/grappler/optimizers/meta_optimizer.cc:788]   constant_folding: Graph size after: 174 nodes (0), 173 edges (0), time = 3.222ms.
2020-06-10 15:19:58.882983: I tensorflow/core/grappler/optimizers/meta_optimizer.cc:788]   arithmetic_optimizer: Graph size after: 174 nodes (0), 173 edges (0), time = 3.627ms.
2020-06-10 15:19:58.882990: I tensorflow/core/grappler/optimizers/meta_optimizer.cc:788]   dependency_optimizer: Graph size after: 174 nodes (0), 173 edges (0), time = 1.035ms.
2020-06-10 15:19:58.882997: I tensorflow/core/grappler/optimizers/meta_optimizer.cc:788]   model_pruner: Graph size after: 174 nodes (0), 173 edges (0), time = 0.742ms.
2020-06-10 15:19:58.883004: I tensorflow/core/grappler/optimizers/meta_optimizer.cc:788]   constant_folding: Graph size after: 174 nodes (0), 173 edges (0), time = 3.115ms.
2020-06-10 15:19:58.883012: I tensorflow/core/grappler/optimizers/meta_optimizer.cc:788]   arithmetic_optimizer: Graph size after: 174 nodes (0), 173 edges (0), time = 3.579ms.
2020-06-10 15:19:58.883019: I tensorflow/core/grappler/optimizers/meta_optimizer.cc:788]   dependency_optimizer: Graph size after: 174 nodes (0), 173 edges (0), time = 1.055ms.
2020-06-10 15:19:58.923034: I tensorflow/core/grappler/optimizers/meta_optimizer.cc:786] Optimization results for grappler item: graph_to_optimize
2020-06-10 15:19:58.923056: I tensorflow/core/grappler/optimizers/meta_optimizer.cc:788]   remapper: Graph size after: 173 nodes (-1), 172 edges (-1), time = 0.691ms.
2020-06-10 15:19:58.923080: I tensorflow/core/grappler/optimizers/meta_optimizer.cc:788]   constant_folding: Graph size after: 173 nodes (0), 172 edges (0), time = 3.373ms.
2020-06-10 15:19:58.923088: I tensorflow/core/grappler/optimizers/meta_optimizer.cc:788]   arithmetic_optimizer: Graph size after: 173 nodes (0), 172 edges (0), time = 3.672ms.
2020-06-10 15:19:58.923095: I tensorflow/core/grappler/optimizers/meta_optimizer.cc:788]   dependency_optimizer: Graph size after: 173 nodes (0), 172 edges (0), time = 0.965ms.
2020-06-10 15:19:58.923102: I tensorflow/core/grappler/optimizers/meta_optimizer.cc:788]   remapper: Graph size after: 173 nodes (0), 172 edges (0), time = 0.734ms.
2020-06-10 15:19:58.923108: I tensorflow/core/grappler/optimizers/meta_optimizer.cc:788]   constant_folding: Graph size after: 173 nodes (0), 172 edges (0), time = 2.933ms.
2020-06-10 15:19:58.923115: I tensorflow/core/grappler/optimizers/meta_optimizer.cc:788]   arithmetic_optimizer: Graph size after: 173 nodes (0), 172 edges (0), time = 3.192ms.
2020-06-10 15:19:58.923122: I tensorflow/core/grappler/optimizers/meta_optimizer.cc:788]   dependency_optimizer: Graph size after: 173 nodes (0), 172 edges (0), time = 1.091ms.
Writing weight file tf_files/web/model.json...
log
复制代码

 

模型压缩

1.9M压缩为953kb,压缩率大约50%。

复制代码
$ gzip -k tf_files/web/
group1-shard1of1.bin  model.json   
jeffrey@unsw
-ThinkPad-T490:retrain-mobilenet-v1$ gzip -k tf_files/web/group1-shard1of1.bin jeffrey@unsw-ThinkPad-T490:retrain-mobilenet-v1$ ll tf_files/web/ total 2836 drwxr-xr-x 2 jeffrey jeffrey 4096 Jun 10 15:24 ./ drwxr-xr-x 7 jeffrey jeffrey 4096 Jun 10 15:19 ../ -rw-r--r-- 1 jeffrey jeffrey 1883348 Jun 10 15:19 group1-shard1of1.bin -rw-r--r-- 1 jeffrey jeffrey 953972 Jun 10 15:19 group1-shard1of1.bin.gz -rw-r--r-- 1 jeffrey jeffrey 56654 Jun 10 15:19 model.json
复制代码

 继续压缩:70.3kB

复制代码
jeffrey@unsw-ThinkPad-T490:differ2_pb$ xxd quantized_graph.pb > file1.hex
jeffrey@unsw-ThinkPad-T490:differ2_pb$ xxd quantized_graph_old.pb > file2.hex
jeffrey@unsw-ThinkPad-T490:differ2_pb$ diff file1.hex file2.hex > log.diff
jeffrey@unsw-ThinkPad-T490:differ2_pb$ ll
total 30988
drwxr-xr-x  2 jeffrey jeffrey     4096 Jun 10 15:49 ./
drwxr-xr-x 10 jeffrey jeffrey     4096 Jun 10 15:43 ../
-rw-r--r--  1 jeffrey jeffrey  8534130 Jun 10 15:49 file1.hex
-rw-r--r--  1 jeffrey jeffrey  8534130 Jun 10 15:49 file2.hex
-rw-r--r--  1 jeffrey jeffrey 10628750 Jun 10 15:49 log.diff
-rw-r--r--  1 jeffrey jeffrey  2008026 Jun 10 15:44 quantized_graph_old.pb
-rw-r--r--  1 jeffrey jeffrey  2008026 Jun 10 15:41 quantized_graph.pb
pb策略
复制代码
复制代码
jeffrey@unsw-ThinkPad-T490:differ1_bin$ ll
total 19400
drwxr-xr-x  2 jeffrey jeffrey    4096 Jun 10 15:48 ./
drwxr-xr-x 10 jeffrey jeffrey    4096 Jun 10 15:43 ../
-rw-r--r--  1 jeffrey jeffrey 8004268 Jun 10 15:47 file1.hex
-rw-r--r--  1 jeffrey jeffrey 8004268 Jun 10 15:48 file2.hex
-rw-r--r--  1 jeffrey jeffrey 1883348 Jun 10 15:42 group1-shard1of1.bin
-rw-r--r--  1 jeffrey jeffrey 1883348 Jun 10 15:19 group1-shard1of1_old.bin
-rw-r--r--  1 jeffrey jeffrey   70316 Jun 10 15:48 log.diff
bin策略
复制代码

 

Add labels

# Install 'jq' once with homebrew (https://brew.sh/)
brew install jq

# Create JSON file from newline-delimited text file
cat tf_files/retrained_labels.txt | jq -Rsc '. / "\n" - [""]' > tf_files/web/labels.json
cat tf_files/web/labels.json

 

五、Deploy on the web

tensorflowjs_model.pb, weights_manifest.json如何来的?

 

此项目链接web page demo有问题,需看最新的readme,如下:

https://github.com/tensorflow/tfjs-examples/tree/master/mobilenet [可用]

https://github.com/tensorflow/tfjs/tree/master/tfjs-converter/demo/mobilenet [命令参考]

复制代码
$ npm install
$ npm run build
> tfjs-examples-mobilenet@0.1.0 build /home/jeffrey/Desktop/tfjs-examples-master/mobilenet > cross-env NODE_ENV=production parcel build index.html --no-minify --public-url ./ ✨ Built in 1.75s. dist/mobilenet.1fda360a.map ⚠️ 5.1 MB 472ms dist/mobilenet.ea2308db.js ⚠️ 1.34 MB 1.07s dist/tfjs-examples.c3b5157d.css 280.77 KB 7ms dist/cat.d8f4b943.jpg 40.08 KB 8ms dist/index.html 2.5 KB 3ms

jeff@unsw
-ThinkPad-T490:mobilenet$ ls cat.jpg dist imagenet_classes.js index.html index.js node_modules package.json README.md yarn.lock
复制代码

 

我们就可以像打开静态网页一样打开我们完成的项目。

打包生成的dist文件夹,其中index.html为入口文件。

 

六、布置静态网页

Ref: Ubuntu 18.04系统中使用Apache搭建简单的web服务器

安装命令:

sudo apt install apache2 -y

 

启动 Web 服务器:

复制代码
jeffrey@unsw-ThinkPad-T490:~$ systemctl status apache2
systemctl: /usr/local/anaconda3/lib/libuuid.so.1: no version information available (required by /lib/x86_64-linux-gnu/libcryptsetup.so.12)
systemctl: /usr/local/anaconda3/lib/libuuid.so.1: no version information available (required by /lib/x86_64-linux-gnu/libblkid.so.1)
● apache2.service - The Apache HTTP Server
   Loaded: loaded (/lib/systemd/system/apache2.service; enabled; vendor preset: 
  Drop-In: /lib/systemd/system/apache2.service.d
           └─apache2-systemd.conf
   Active: active (running) since Thu 2020-06-11 09:54:41 AEST; 10s ago
 Main PID: 31523 (apache2)
    Tasks: 55 (limit: 4915)
   CGroup: /system.slice/apache2.service
           ├─31523 /usr/sbin/apache2 -k start
           ├─31524 /usr/sbin/apache2 -k start
           └─31525 /usr/sbin/apache2 -k start

jeffrey@unsw-ThinkPad-T490:~$ 
jeffrey@unsw-ThinkPad-T490:~$ 
jeffrey@unsw-ThinkPad-T490:~$ /etc/init.d/apache2 start
[....] Starting apache2 (via systemctl): apache2.service/bin/systemctl: /usr/local/anaconda3/lib/libuuid.so.1: no version information available (required by /lib/x86_64-linux-gnu/libcryptsetup.so.12)
/bin/systemctl: /usr/local/anaconda3/lib/libuuid.so.1: no version information available (required by /lib/x86_64-linux-gnu/libblkid.so.1)
/bin/systemd-tty-ask-password-agent: /usr/local/anaconda3/lib/libuuid.so.1: no version information available (required by /lib/x86_64-linux-gnu/libcryptsetup.so.12)
/bin/systemd-tty-ask-password-agent: /usr/local/anaconda3/lib/libuuid.so.1: no version information available (required by /lib/x86_64-linux-gnu/libblkid.so.1)
. ok 
jeffrey@unsw-ThinkPad-T490:~$ 
jeffrey@unsw-ThinkPad-T490:~$ 
jeffrey@unsw-ThinkPad-T490:~$ /etc/init.d/apache2 stop
[....] Stopping apache2 (via systemctl): apache2.service/bin/systemctl: /usr/local/anaconda3/lib/libuuid.so.1: no version information available (required by /lib/x86_64-linux-gnu/libcryptsetup.so.12)
/bin/systemctl: /usr/local/anaconda3/lib/libuuid.so.1: no version information available (required by /lib/x86_64-linux-gnu/libblkid.so.1)
/bin/systemd-tty-ask-password-agent: /usr/local/anaconda3/lib/libuuid.so.1: no version information available (required by /lib/x86_64-linux-gnu/libcryptsetup.so.12)
/bin/systemd-tty-ask-password-agent: /usr/local/anaconda3/lib/libuuid.so.1: no version information available (required by /lib/x86_64-linux-gnu/libblkid.so.1)
. ok 
jeffrey@unsw-ThinkPad-T490:~$ 
jeffrey@unsw-ThinkPad-T490:~$ 
jeffrey@unsw-ThinkPad-T490:~$ /etc/init.d/apache2 restart
[....] Restarting apache2 (via systemctl): apache2.service/bin/systemctl: /usr/local/anaconda3/lib/libuuid.so.1: no version information available (required by /lib/x86_64-linux-gnu/libcryptsetup.so.12)
/bin/systemctl: /usr/local/anaconda3/lib/libuuid.so.1: no version information available (required by /lib/x86_64-linux-gnu/libblkid.so.1)
/bin/systemd-tty-ask-password-agent: /usr/local/anaconda3/lib/libuuid.so.1: no version information available (required by /lib/x86_64-linux-gnu/libcryptsetup.so.12)
/bin/systemd-tty-ask-password-agent: /usr/local/anaconda3/lib/libuuid.so.1: no version information available (required by /lib/x86_64-linux-gnu/libblkid.so.1)
. ok 
复制代码

 

遇到CORS问题:

$ google-chrome --disable-web-security

 

修改 Nginx 后台配置文件

Nginx命令参考:如何在Ubuntu 18.04上安装Nginx

现在您的Web服务器已启动并运行,让我们来回顾一些基本的管理命令。

复制代码
要停止您的Web服务器,请键入:
sudo systemctl stop nginx

停止时要启动Web服务器,请输入:
sudo systemctl start nginx

要停止并再次启动服务,请键入:
sudo systemctl restart nginx

如果您只是简单地进行配置更改,Nginx通常可以重新加载而不会丢失连接。 为此,请输入:
sudo systemctl reload nginx

默认情况下,Nginx配置为在服务器引导时自动启动。 如果这不是您想要的,可以通过输入以下命令来禁用此行为:
sudo systemctl disable nginx

要重新启用服务以在启动时启动,您可以键入:
sudo systemctl enable nginx
复制代码

 

Nginx配置实例

实例一:允许example.com的应用在www.example2.com上跨域提取数据

在nginx.conf里找到server项,并在里面添加如下配置

1
2
3
4
5
6
7
8
location /{

add_header 'Access-Control-Allow-Origin' 'http://example.com';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Headers' 'Authorization,Content-Type,Accept,Origin,User-Agent,DNT,Cache-Control,X-Mx-ReqToken,X-Requested-With';
add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS';
...
}

如果需要允许来自任何域的访问,可以这样配置

1
add_header Access-Control-Allow-Origin *;

 

End.

posted @   郝壹贰叁  阅读(372)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示