Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Making inference thread-safe by avoiding mutable state in Detect class #2120

Merged
merged 45 commits into from
Feb 26, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
03fae98
Avoid mutable state in Detect
Feb 2, 2021
4bdc5a3
LoadImages() pathlib update (#2140)
glenn-jocher Feb 5, 2021
e9b3de4
Unique *.cache filenames fix (#2134)
train255 Feb 5, 2021
86897e3
Update train.py test batch_size (#2148)
glenn-jocher Feb 6, 2021
ad839ed
Update train.py (#2149)
glenn-jocher Feb 6, 2021
6b634c6
Linear LR scheduler option (#2150)
glenn-jocher Feb 6, 2021
a5359f6
Update data-autodownload background tasks (#2154)
glenn-jocher Feb 7, 2021
c32b0af
Update detect.py (#2167)
ab-101 Feb 9, 2021
ace3e02
Update requirements.txt (#2173)
glenn-jocher Feb 10, 2021
c9bda11
Update utils/datasets.py to support .webp files (#2174)
Transigent Feb 10, 2021
a5d5f92
Changed socket port and added timeout (#2176)
NanoCode012 Feb 10, 2021
404749a
PyTorch Hub results.save('path/to/dir') (#2179)
glenn-jocher Feb 11, 2021
bdd88e1
YOLOv5 Segmentation Dataloader Updates (#2188)
glenn-jocher Feb 12, 2021
17ac94b
Created using Colaboratory
glenn-jocher Feb 12, 2021
3e560e2
YOLOv5 PyTorch Hub results.save() method retains filenames (#2194)
dan0nchik Feb 12, 2021
3ff783c
TTA augument boxes one pixel shifted in de-flip ud and lr (#2219)
VdLMV Feb 15, 2021
7b833e3
LoadStreams() frame loss bug fix (#2222)
glenn-jocher Feb 15, 2021
f8464b4
Update yolo.py channel array (#2223)
glenn-jocher Feb 16, 2021
26c2e54
Add check_imshow() (#2231)
glenn-jocher Feb 16, 2021
5a40ce6
Update CI badge (#2230)
glenn-jocher Feb 16, 2021
d2e754b
Add isdocker() (#2232)
glenn-jocher Feb 16, 2021
9d87307
YOLOv5 Hub URL inference bug fix (#2250)
glenn-jocher Feb 19, 2021
db28ce6
Improved hubconf.py CI tests (#2251)
glenn-jocher Feb 19, 2021
5f42643
Unified hub and detect.py box and labels plotting (#2243)
kinoute Feb 19, 2021
47faf95
reset head
glenn-jocher Feb 19, 2021
ab2da5e
Merge remote-tracking branch 'origin/master'
glenn-jocher Feb 19, 2021
c09964c
Update inference default to multi_label=False (#2252)
glenn-jocher Feb 19, 2021
6f5d6fc
Robust objectness loss balancing (#2256)
glenn-jocher Feb 20, 2021
095d2c1
Created using Colaboratory
glenn-jocher Feb 20, 2021
e27ca0d
Update minimum stride to 32 (#2266)
glenn-jocher Feb 22, 2021
95aefea
Dynamic ONNX engine generation (#2208)
aditya-dl Feb 22, 2021
32dd161
Update greetings.yml for auto-rebase on PR (#2272)
glenn-jocher Feb 23, 2021
cc79f3a
Update Dockerfile with apt install zip (#2274)
glenn-jocher Feb 23, 2021
83dc1b4
FLOPS min stride 32 (#2276)
xiaowo1996 Feb 23, 2021
7a6870b
Update README.md
glenn-jocher Feb 23, 2021
d5d275b
Amazon AWS EC2 startup and re-startup scripts (#2185)
glenn-jocher Feb 24, 2021
0070995
Amazon AWS EC2 startup and re-startup scripts (#2282)
glenn-jocher Feb 24, 2021
ca5b10b
Update train.py (#2290)
glenn-jocher Feb 24, 2021
ec1d849
Improved model+EMA checkpointing (#2292)
glenn-jocher Feb 25, 2021
71dd276
Improved model+EMA checkpointing 2 (#2295)
glenn-jocher Feb 25, 2021
a82dce7
Fix labels being missed when image extension appears twice in filenam…
idenc Feb 26, 2021
efa4946
W&B entity support (#2298)
toretak Feb 26, 2021
8304b90
Avoid mutable state in Detect
Feb 2, 2021
558df86
Merge branch 'threadsafe' of https://github.com/olehb/yolov5 into thr…
glenn-jocher Feb 26, 2021
c89d671
Update yolo and remove .to(device)
glenn-jocher Feb 26, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/greetings.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
repo-token: ${{ secrets.GITHUB_TOKEN }}
pr-message: |
πŸ‘‹ Hello @${{ github.actor }}, thank you for submitting a πŸš€ PR! To allow your work to be integrated as seamlessly as possible, we advise you to:
- βœ… Verify your PR is **up-to-date with origin/master.** If your PR is behind origin/master update by running the following, replacing 'feature' with the name of your local branch:
- βœ… Verify your PR is **up-to-date with origin/master.** If your PR is behind origin/master an automatic [GitHub actions](https://github.com/ultralytics/yolov5/blob/master/.github/workflows/rebase.yml) rebase may be attempted by including the /rebase command in a comment body, or by running the following code, replacing 'feature' with the name of your local branch:
```bash
git remote add upstream https://github.com/ultralytics/yolov5.git
git fetch upstream
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
FROM nvcr.io/nvidia/pytorch:20.12-py3

# Install linux packages
RUN apt update && apt install -y screen libgl1-mesa-glx
RUN apt update && apt install -y zip screen libgl1-mesa-glx

# Install python dependencies
RUN python -m pip install --upgrade pip
Expand Down
27 changes: 12 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<img src="https://user-images.githubusercontent.com/26833433/98699617-a1595a00-2377-11eb-8145-fc674eb9b1a7.jpg" width="1000"></a>
&nbsp

![CI CPU testing](https://github.com/ultralytics/yolov5/workflows/CI%20CPU%20testing/badge.svg)
<a href="https://github.com/ultralytics/yolov5/actions"><img src="https://github.com/ultralytics/yolov5/workflows/CI%20CPU%20testing/badge.svg" alt="CI CPU testing"></a>

This repository represents Ultralytics open-source research into future object detection methods, and incorporates lessons learned and best practices evolved over thousands of hours of training and evolution on anonymized client datasets. **All code and models are under active development, and are subject to modification or deletion without notice.** Use at your own risk.

Expand Down Expand Up @@ -89,17 +89,15 @@ To run inference on example images in `data/images`:
```bash
$ python detect.py --source data/images --weights yolov5s.pt --conf 0.25

Namespace(agnostic_nms=False, augment=False, classes=None, conf_thres=0.25, device='', img_size=640, iou_thres=0.45, save_conf=False, save_dir='runs/detect', save_txt=False, source='data/images/', update=False, view_img=False, weights=['yolov5s.pt'])
Using torch 1.7.0+cu101 CUDA:0 (Tesla V100-SXM2-16GB, 16130MB)

Downloading https://github.com/ultralytics/yolov5/releases/download/v3.1/yolov5s.pt to yolov5s.pt... 100%|β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ| 14.5M/14.5M [00:00<00:00, 21.3MB/s]
Namespace(agnostic_nms=False, augment=False, classes=None, conf_thres=0.25, device='', exist_ok=False, img_size=640, iou_thres=0.45, name='exp', project='runs/detect', save_conf=False, save_txt=False, source='data/images/', update=False, view_img=False, weights=['yolov5s.pt'])
YOLOv5 v4.0-96-g83dc1b4 torch 1.7.0+cu101 CUDA:0 (Tesla V100-SXM2-16GB, 16160.5MB)

Fusing layers...
Model Summary: 232 layers, 7459581 parameters, 0 gradients
image 1/2 data/images/bus.jpg: 640x480 4 persons, 1 buss, 1 skateboards, Done. (0.012s)
image 2/2 data/images/zidane.jpg: 384x640 2 persons, 2 ties, Done. (0.012s)
Results saved to runs/detect/exp
Done. (0.113s)
Model Summary: 224 layers, 7266973 parameters, 0 gradients, 17.0 GFLOPS
image 1/2 /content/yolov5/data/images/bus.jpg: 640x480 4 persons, 1 bus, Done. (0.010s)
image 2/2 /content/yolov5/data/images/zidane.jpg: 384x640 2 persons, 1 tie, Done. (0.011s)
Results saved to runs/detect/exp2
Done. (0.103s)
```
<img src="https://user-images.githubusercontent.com/26833433/97107365-685a8d80-16c7-11eb-8c2e-83aac701d8b9.jpeg" width="500">

Expand All @@ -108,18 +106,17 @@ Done. (0.113s)
To run **batched inference** with YOLOv5 and [PyTorch Hub](https://github.com/ultralytics/yolov5/issues/36):
```python
import torch
from PIL import Image

# Model
model = torch.hub.load('ultralytics/yolov5', 'yolov5s', pretrained=True)

# Images
img1 = Image.open('zidane.jpg')
img2 = Image.open('bus.jpg')
imgs = [img1, img2] # batched list of images
dir = 'https://github.com/ultralytics/yolov5/raw/master/data/images/'
imgs = [dir + f for f in ('zidane.jpg', 'bus.jpg')] # batched list of images

# Inference
result = model(imgs)
results = model(imgs)
results.print() # or .show(), .save()
```


Expand Down
9 changes: 5 additions & 4 deletions data/scripts/get_coco.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@
# Download/unzip labels
d='../' # unzip directory
url=https://github.com/ultralytics/yolov5/releases/download/v1.0/
f='coco2017labels.zip' # 68 MB
echo 'Downloading' $url$f ' ...' && curl -L $url$f -o $f && unzip -q $f -d $d && rm $f # download, unzip, remove
f='coco2017labels.zip' # or 'coco2017labels-segments.zip', 68 MB
echo 'Downloading' $url$f ' ...'
curl -L $url$f -o $f && unzip -q $f -d $d && rm $f & # download, unzip, remove in background

# Download/unzip images
d='../coco/images' # unzip directory
Expand All @@ -20,7 +21,7 @@ f1='train2017.zip' # 19G, 118k images
f2='val2017.zip' # 1G, 5k images
f3='test2017.zip' # 7G, 41k images (optional)
for f in $f1 $f2; do
echo 'Downloading' $url$f '...' && curl -L $url$f -o $f # download, (unzip, remove in background)
unzip -q $f -d $d && rm $f &
echo 'Downloading' $url$f '...'
curl -L $url$f -o $f && unzip -q $f -d $d && rm $f & # download, unzip, remove in background
done
wait # finish background tasks
4 changes: 2 additions & 2 deletions data/scripts/get_voc.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ f1=VOCtrainval_06-Nov-2007.zip # 446MB, 5012 images
f2=VOCtest_06-Nov-2007.zip # 438MB, 4953 images
f3=VOCtrainval_11-May-2012.zip # 1.95GB, 17126 images
for f in $f3 $f2 $f1; do
echo 'Downloading' $url$f '...' && curl -L $url$f -o $f # download, (unzip, remove in background)
unzip -q $f -d $d && rm $f &
echo 'Downloading' $url$f '...'
curl -L $url$f -o $f && unzip -q $f -d $d && rm $f & # download, unzip, remove in background
done
wait # finish background tasks

Expand Down
7 changes: 4 additions & 3 deletions detect.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@

from models.experimental import attempt_load
from utils.datasets import LoadStreams, LoadImages
from utils.general import check_img_size, check_requirements, non_max_suppression, apply_classifier, scale_coords, \
xyxy2xywh, strip_optimizer, set_logging, increment_path
from utils.general import check_img_size, check_requirements, check_imshow, non_max_suppression, apply_classifier, \
scale_coords, xyxy2xywh, strip_optimizer, set_logging, increment_path
from utils.plots import plot_one_box
from utils.torch_utils import select_device, load_classifier, time_synchronized

Expand Down Expand Up @@ -45,7 +45,7 @@ def detect(save_img=False):
# Set Dataloader
vid_path, vid_writer = None, None
if webcam:
view_img = True
view_img = check_imshow()
cudnn.benchmark = True # set True to speed up constant image size inference
dataset = LoadStreams(source, img_size=imgsz, stride=stride)
else:
Expand Down Expand Up @@ -118,6 +118,7 @@ def detect(save_img=False):
# Stream results
if view_img:
cv2.imshow(str(p), im0)
cv2.waitKey(1) # 1 millisecond

# Save results (image with detections)
if save_img:
Expand Down
9 changes: 7 additions & 2 deletions hubconf.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,9 +133,14 @@ def custom(path_or_model='path/to/model.pt', autoshape=True):
# model = custom(path_or_model='path/to/model.pt') # custom example

# Verify inference
import numpy as np
from PIL import Image

imgs = [Image.open(x) for x in Path('data/images').glob('*.jpg')]
results = model(imgs)
imgs = [Image.open('data/images/bus.jpg'), # PIL
'data/images/zidane.jpg', # filename
'https://github.com/ultralytics/yolov5/raw/master/data/images/bus.jpg', # URI
np.zeros((640, 480, 3))] # numpy

results = model(imgs) # batched inference
results.print()
results.save()
33 changes: 19 additions & 14 deletions models/common.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
# This file contains modules common to various models

import math
from pathlib import Path

import numpy as np
import requests
import torch
import torch.nn as nn
from PIL import Image, ImageDraw
from PIL import Image

from utils.datasets import letterbox
from utils.general import non_max_suppression, make_divisible, scale_coords, xyxy2xywh
from utils.plots import color_list
from utils.plots import color_list, plot_one_box


def autopad(k, p=None): # kernel, padding
Expand Down Expand Up @@ -195,10 +196,12 @@ def forward(self, imgs, size=640, augment=False, profile=False):

# Pre-process
n, imgs = (len(imgs), imgs) if isinstance(imgs, list) else (1, [imgs]) # number of images, list of images
shape0, shape1 = [], [] # image and inference shapes
shape0, shape1, files = [], [], [] # image and inference shapes, filenames
for i, im in enumerate(imgs):
if isinstance(im, str): # filename or uri
im = Image.open(requests.get(im, stream=True).raw if im.startswith('http') else im) # open
im, f = Image.open(requests.get(im, stream=True).raw if im.startswith('http') else im), im # open
im.filename = f # for uri
files.append(Path(im.filename).with_suffix('.jpg').name if isinstance(im, Image.Image) else f'image{i}.jpg')
im = np.array(im) # to numpy
if im.shape[0] < 5: # image in CHW
im = im.transpose((1, 2, 0)) # reverse dataloader .transpose(2, 0, 1)
Expand All @@ -223,25 +226,26 @@ def forward(self, imgs, size=640, augment=False, profile=False):
for i in range(n):
scale_coords(shape1, y[i][:, :4], shape0[i])

return Detections(imgs, y, self.names)
return Detections(imgs, y, files, self.names)


class Detections:
# detections class for YOLOv5 inference results
def __init__(self, imgs, pred, names=None):
def __init__(self, imgs, pred, files, names=None):
super(Detections, self).__init__()
d = pred[0].device # device
gn = [torch.tensor([*[im.shape[i] for i in [1, 0, 1, 0]], 1., 1.], device=d) for im in imgs] # normalizations
self.imgs = imgs # list of images as numpy arrays
self.pred = pred # list of tensors pred[0] = (xyxy, conf, cls)
self.names = names # class names
self.files = files # image filenames
self.xyxy = pred # xyxy pixels
self.xywh = [xyxy2xywh(x) for x in pred] # xywh pixels
self.xyxyn = [x / g for x, g in zip(self.xyxy, gn)] # xyxy normalized
self.xywhn = [x / g for x, g in zip(self.xywh, gn)] # xywh normalized
self.n = len(self.pred)

def display(self, pprint=False, show=False, save=False, render=False):
def display(self, pprint=False, show=False, save=False, render=False, save_dir=''):
colors = color_list()
for i, (img, pred) in enumerate(zip(self.imgs, self.pred)):
str = f'image {i + 1}/{len(self.pred)}: {img.shape[0]}x{img.shape[1]} '
Expand All @@ -250,16 +254,16 @@ def display(self, pprint=False, show=False, save=False, render=False):
n = (pred[:, -1] == c).sum() # detections per class
str += f"{n} {self.names[int(c)]}{'s' * (n > 1)}, " # add to string
if show or save or render:
img = Image.fromarray(img.astype(np.uint8)) if isinstance(img, np.ndarray) else img # from np
for *box, conf, cls in pred: # xyxy, confidence, class
# str += '%s %.2f, ' % (names[int(cls)], conf) # label
ImageDraw.Draw(img).rectangle(box, width=4, outline=colors[int(cls) % 10]) # plot
label = f'{self.names[int(cls)]} {conf:.2f}'
plot_one_box(box, img, label=label, color=colors[int(cls) % 10])
img = Image.fromarray(img.astype(np.uint8)) if isinstance(img, np.ndarray) else img # from np
if pprint:
print(str.rstrip(', '))
if show:
img.show(f'image {i}') # show
img.show(self.files[i]) # show
if save:
f = f'results{i}.jpg'
f = Path(save_dir) / self.files[i]
img.save(f) # save
print(f"{'Saving' * (i == 0)} {f},", end='' if i < self.n - 1 else ' done.\n')
if render:
Expand All @@ -271,8 +275,9 @@ def print(self):
def show(self):
self.display(show=True) # show results

def save(self):
self.display(save=True) # save results
def save(self, save_dir='results/'):
Path(save_dir).mkdir(exist_ok=True)
self.display(save=True, save_dir=save_dir) # save results

def render(self):
self.display(render=True) # render results
Expand Down
5 changes: 4 additions & 1 deletion models/export.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
parser = argparse.ArgumentParser()
parser.add_argument('--weights', type=str, default='./yolov5s.pt', help='weights path') # from yolov5/models/
parser.add_argument('--img-size', nargs='+', type=int, default=[640, 640], help='image size') # height, width
parser.add_argument('--dynamic', action='store_true', help='dynamic ONNX axes')
parser.add_argument('--batch-size', type=int, default=1, help='batch size')
opt = parser.parse_args()
opt.img_size *= 2 if len(opt.img_size) == 1 else 1 # expand
Expand Down Expand Up @@ -70,7 +71,9 @@
print('\nStarting ONNX export with onnx %s...' % onnx.__version__)
f = opt.weights.replace('.pt', '.onnx') # filename
torch.onnx.export(model, img, f, verbose=False, opset_version=12, input_names=['images'],
output_names=['classes', 'boxes'] if y is None else ['output'])
output_names=['classes', 'boxes'] if y is None else ['output'],
dynamic_axes={'images': {0: 'batch', 2: 'height', 3: 'width'}, # size(1,3,640,640)
'output': {0: 'batch', 2: 'y', 3: 'x'}} if opt.dynamic else None)

# Checks
onnx_model = onnx.load(f) # load onnx model
Expand Down
41 changes: 13 additions & 28 deletions models/yolo.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
import logging
import sys
from copy import deepcopy
from pathlib import Path

sys.path.append('./') # to run '$ python *.py' files in subdirectories
logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -50,7 +49,7 @@ def forward(self, x):
self.grid[i] = self._make_grid(nx, ny).to(x[i].device)

y = x[i].sigmoid()
y[..., 0:2] = (y[..., 0:2] * 2. - 0.5 + self.grid[i].to(x[i].device)) * self.stride[i] # xy
y[..., 0:2] = (y[..., 0:2] * 2. - 0.5 + self.grid[i]) * self.stride[i] # xy
y[..., 2:4] = (y[..., 2:4] * 2) ** 2 * self.anchor_grid[i] # wh
z.append(y.view(bs, -1, self.no))

Expand Down Expand Up @@ -110,9 +109,9 @@ def forward(self, x, augment=False, profile=False):
# cv2.imwrite(f'img_{si}.jpg', 255 * xi[0].cpu().numpy().transpose((1, 2, 0))[:, :, ::-1]) # save
yi[..., :4] /= si # de-scale
if fi == 2:
yi[..., 1] = img_size[0] - yi[..., 1] # de-flip ud
yi[..., 1] = img_size[0] - 1 - yi[..., 1] # de-flip ud
elif fi == 3:
yi[..., 0] = img_size[1] - yi[..., 0] # de-flip lr
yi[..., 0] = img_size[1] - 1 - yi[..., 0] # de-flip lr
y.append(yi)
return torch.cat(y, 1), None # augmented inference, train
else:
Expand Down Expand Up @@ -213,43 +212,27 @@ def parse_model(d, ch): # model_dict, input_channels(3)
if m in [Conv, GhostConv, Bottleneck, GhostBottleneck, SPP, DWConv, MixConv2d, Focus, CrossConv, BottleneckCSP,
C3]:
c1, c2 = ch[f], args[0]

# Normal
# if i > 0 and args[0] != no: # channel expansion factor
# ex = 1.75 # exponential (default 2.0)
# e = math.log(c2 / ch[1]) / math.log(2)
# c2 = int(ch[1] * ex ** e)
# if m != Focus:

c2 = make_divisible(c2 * gw, 8) if c2 != no else c2

# Experimental
# if i > 0 and args[0] != no: # channel expansion factor
# ex = 1 + gw # exponential (default 2.0)
# ch1 = 32 # ch[1]
# e = math.log(c2 / ch1) / math.log(2) # level 1-n
# c2 = int(ch1 * ex ** e)
# if m != Focus:
# c2 = make_divisible(c2, 8) if c2 != no else c2
if c2 != no: # if not output
c2 = make_divisible(c2 * gw, 8)

args = [c1, c2, *args[1:]]
if m in [BottleneckCSP, C3]:
args.insert(2, n)
args.insert(2, n) # number of repeats
n = 1
elif m is nn.BatchNorm2d:
args = [ch[f]]
elif m is Concat:
c2 = sum([ch[x if x < 0 else x + 1] for x in f])
c2 = sum([ch[x] for x in f])
elif m is Detect:
args.append([ch[x + 1] for x in f])
args.append([ch[x] for x in f])
if isinstance(args[1], int): # number of anchors
args[1] = [list(range(args[1] * 2))] * len(f)
elif m is Contract:
c2 = ch[f if f < 0 else f + 1] * args[0] ** 2
c2 = ch[f] * args[0] ** 2
elif m is Expand:
c2 = ch[f if f < 0 else f + 1] // args[0] ** 2
c2 = ch[f] // args[0] ** 2
else:
c2 = ch[f if f < 0 else f + 1]
c2 = ch[f]

m_ = nn.Sequential(*[m(*args) for _ in range(n)]) if n > 1 else m(*args) # module
t = str(m)[8:-2].replace('__main__.', '') # module type
Expand All @@ -258,6 +241,8 @@ def parse_model(d, ch): # model_dict, input_channels(3)
logger.info('%3s%18s%3s%10.0f %-40s%-30s' % (i, f, n, np, t, args)) # print
save.extend(x % i for x in ([f] if isinstance(f, int) else f) if x != -1) # append to savelist
layers.append(m_)
if i == 0:
ch = []
ch.append(c2)
return nn.Sequential(*layers), sorted(save)

Expand Down
4 changes: 2 additions & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ seaborn>=0.11.0
pandas

# export --------------------------------------
# coremltools==4.0
# onnx>=1.8.0
# coremltools>=4.1
# onnx>=1.8.1
# scikit-learn==0.19.2 # for coreml quantization

# extras --------------------------------------
Expand Down