Browse Source

[MNT] Modify details

tags/v0.3.2
bxdd 3 years ago
parent
commit
eb56e9ddda
13 changed files with 212 additions and 201 deletions
  1. BIN
      classes.png
  2. BIN
      docs/_static/img/classes.png
  3. +4
    -4
      examples/examples1/example_rkme.py
  4. +7
    -16
      examples/examples2/example_learnware.py
  5. +2
    -2
      examples/examples2/svm/__init__.py
  6. +3
    -3
      learnware/learnware/base.py
  7. +1
    -1
      learnware/market/__init__.py
  8. +9
    -9
      learnware/market/anchor.py
  9. +80
    -59
      learnware/market/base.py
  10. +1
    -1
      learnware/market/evolve.py
  11. +4
    -4
      learnware/specification/base.py
  12. +63
    -64
      learnware/specification/rkme.py
  13. +38
    -38
      learnware/specification/utils.py

BIN
classes.png View File

Before After
Width: 2925  |  Height: 840  |  Size: 200 kB Width: 3361  |  Height: 980  |  Size: 258 kB

BIN
docs/_static/img/classes.png View File

Before After
Width: 2925  |  Height: 840  |  Size: 200 kB Width: 3361  |  Height: 980  |  Size: 258 kB

+ 4
- 4
examples/examples1/example_rkme.py View File

@@ -10,17 +10,17 @@ if __name__ == "__main__":
spec2 = specification.rkme.RKMESpecification()
spec1.generate_stat_spec_from_data(data_X)
spec1.save("spec.json")
beta = spec1.get_beta()
z = spec1.get_z()
print(type(beta), beta.shape)
print(type(z), z.shape)
spec2.load("spec.json")
beta = spec1.get_beta()
z = spec1.get_z()
print(type(beta), beta.shape)
print(type(z), z.shape)
print(spec1.inner_prod(spec2))
print(spec1.dist(spec2))
print(spec1.dist(spec2))

+ 7
- 16
examples/examples2/example_learnware.py View File

@@ -16,30 +16,21 @@ def prepare_learnware():
clf = svm.SVC()
clf.fit(data_X, data_y)
joblib.dump(clf, "./svm/svm.pkl")
spec= specification.utils.generate_rkme_spec(X=data_X, gamma=0.1, cuda_idx=0)
spec = specification.utils.generate_rkme_spec(X=data_X, gamma=0.1, cuda_idx=0)
spec.save("./svm/spec.json")


def test_API():
text_X = np.random.randn(100, 20)
text_X = np.random.randn(100, 20)
svm = SVM()
pred_y1 = svm.predict(text_X)
print(type(svm))
model = {
"module_path": "./svm/__init__.py",
"class_name": "SVM"
}

model = {"module_path": "./svm/__init__.py", "class_name": "SVM"}
spec = specification.rkme.RKMESpecification()
spec.load("./svm/spec.json")
learnware = Learnware(
id="A0",
name="SVM",
model=model,
specification=spec,
desc="svm"
)
learnware = Learnware(id="A0", name="SVM", model=model, specification=spec, desc="svm")
pred_y2 = learnware.predict(text_X)
print(type(learnware.model))
print(f"diff: {np.sum(pred_y1 != pred_y2)}")
@@ -47,4 +38,4 @@ def test_API():

if __name__ == "__main__":
prepare_learnware()
test_API()
test_API()

+ 2
- 2
examples/examples2/svm/__init__.py View File

@@ -6,7 +6,7 @@ import numpy as np
class SVM:
def __init__(self):
dir_path = os.path.dirname(os.path.abspath(__file__))
self.model = joblib.load(os.path.join(dir_path, 'svm.pkl'))
self.model = joblib.load(os.path.join(dir_path, "svm.pkl"))

def fit(self, X: np.ndarray, y: np.ndarray):
pass
@@ -15,4 +15,4 @@ class SVM:
return self.model.predict(X)

def fintune(self, X: np.ndarray, y: np.ndarray):
pass
pass

+ 3
- 3
learnware/learnware/base.py View File

@@ -48,7 +48,7 @@ class Learnware:

def predict(self, X: np.ndarray) -> np.ndarray:
return self.model.predict(X)
def get_model(self) -> BaseModel:
return self.model

@@ -57,10 +57,10 @@ class Learnware:

def get_info(self):
return self.desc
def update_stat_spec(self, name, new_stat_spec: BaseStatSpecification):
self.specification.update_stat_spec(name, new_stat_spec)
def update(self):
# Empty Interface.
raise NotImplementedError("'update' Method is NOT Implemented.")

+ 1
- 1
learnware/market/__init__.py View File

@@ -1,3 +1,3 @@
from .base import BaseUserInfo, BaseMarket
from .anchor import AnchoredUserInfo, AnchoredMarket
from .evolve import EvolvedMarket
from .evolve import EvolvedMarket

+ 9
- 9
learnware/market/anchor.py View File

@@ -7,14 +7,14 @@ from .base import BaseUserInfo, BaseMarket

class AnchoredUserInfo(BaseUserInfo):
"""
User Information for searching learnware (add the anchor design)
- UserInfo contains the anchor list acquired from the market
- UserInfo can update stat_info based on anchors
User Information for searching learnware (add the anchor design)
- UserInfo contains the anchor list acquired from the market
- UserInfo can update stat_info based on anchors
"""

def __init__(self, id: str, property: dict = dict(), stat_info: dict = dict()):
super(AnchoredUserInfo, self).__init__(id, property, stat_info)
def __init__(self, id: str, semantic_spec: dict = dict(), stat_info: dict = dict()):
super(AnchoredUserInfo, self).__init__(id, semantic_spec, stat_info)
self.anchor_learnware_list = {} # id: Learnware

def add_anchor_learnware(self, learnware_id: str, learnware: Learnware):
@@ -79,7 +79,7 @@ class AnchoredMarket(BaseMarket):
-------
bool
True if the target anchor learnware is deleted successfully.
Raises
------
Exception
@@ -107,7 +107,7 @@ class AnchoredMarket(BaseMarket):
Parameters
----------
user_info : AnchoredUserInfo
- user_info with properties and statistical information
- user_info with semantic specifications and statistical information
- some statistical information calculated on previous anchor learnwares

Returns
@@ -126,7 +126,7 @@ class AnchoredMarket(BaseMarket):
Parameters
----------
user_info : AnchoredUserInfo
- user_info with properties and statistical information
- user_info with semantic specifications and statistical information
- some statistical information calculated on anchor learnwares

Returns


+ 80
- 59
learnware/market/base.py View File

@@ -7,39 +7,33 @@ from ..learnware import Learnware


class BaseUserInfo:
"""
User Information for searching learnware
- Return random learnwares when both property and stat_info is empty
- Search only based on property when stat_info is None
- Filter through property and rank according to stat_info otherwise
"""
"""User Information for searching learnware"""

def __init__(self, id: str, property: dict = dict(), stat_info: dict = dict()):
def __init__(self, id: str, semantic_spec: dict = dict(), stat_info: dict = dict()):
"""Initializing user information

Parameters
----------
id : str
user id
property : dict, optional
property selected by user, by default dict()
semantic_spec : dict, optional
semantic_spec selected by user, by default dict()
stat_info : dict, optional
statistical information uploaded by user, by default dict()
"""
self.id = id
self.property = property
self.semantic_spec = semantic_spec
self.stat_info = stat_info

def get_property(self) -> dict:
"""Return user properties
def get_semantic_spec(self) -> dict:
"""Return user semantic specifications

Returns
-------
dict
user properties
user semantic specifications
"""
return self.property
return self.semantic_spec

def get_stat_info(self, name: str):
return self.stat_info.get(name, None)
@@ -58,38 +52,64 @@ class BaseMarket:
"""Initializing an empty market"""
self.learnware_list = {} # id: Learnware
self.count = 0
self.property_list = {
'Data': {
'Values': ['Tabular', 'Image', 'Video', 'Text', 'Audio'],
'Type' : 'Class', # Choose only one class
self.semantic_spec_list = self._init_semantic_spec_list()

def _init_semantic_spec_list(self):
return {
"Data": {
"Values": ["Tabular", "Image", "Video", "Text", "Audio"],
"Type": "Class", # Choose only one class
},
'Task': {
'Values': ['Classification','Regression','Clustering','Feature Extraction','Generation','Segmentation','Object Detection'],
'Type': 'Class', # Choose only one class
"Task": {
"Values": [
"Classification",
"Regression",
"Clustering",
"Feature Extraction",
"Generation",
"Segmentation",
"Object Detection",
],
"Type": "Class", # Choose only one class
},
'Device': {
'Values': ['CPU', 'GPU'],
'Type': 'Tag', # Choose one or more tags
"Device": {
"Values": ["CPU", "GPU"],
"Type": "Tag", # Choose one or more tags
},
'Scenario': {
'Values': ['Business', 'Financial', 'Health', 'Politics', 'Computer', 'Internet', 'Traffic', 'Nature', 'Fashion', 'Industry', 'Agriculture', 'Education', 'Entertainment', 'Architecture'],
'Type': 'Tag', # Choose one or more tags
"Scenario": {
"Values": [
"Business",
"Financial",
"Health",
"Politics",
"Computer",
"Internet",
"Traffic",
"Nature",
"Fashion",
"Industry",
"Agriculture",
"Education",
"Entertainment",
"Architecture",
],
"Type": "Tag", # Choose one or more tags
},
'Description': {
'Values': str,
'Type': 'Description',
"Description": {
"Values": str,
"Type": "Description",
},
}

def reload_market(self, market_path: str, property_list_path: str, load_mode: str = "database") -> bool:
def reload_market(self, market_path: str, semantic_spec_list_path: str, load_mode: str = "database") -> bool:
"""Reload the market when server restared.

Parameters
----------
market_path : str
Directory for market data. '_IP_:_port_' for loading from database.
property_list_path : str
Directory for available property. Should be a json file.
semantic_spec_list_path : str
Directory for available semantic_spec. Should be a json file.
load_mode : str, optional
Type of reload source. Currently, only 'database' is available. Defaults to 'database', by default "database"

@@ -103,7 +123,7 @@ class BaseMarket:
NotImplementedError
Reload method NOT implemented. Currently, only loading from database is supported.
FileNotFoundError
Loading source/property_list NOT found. Check whether the source and property_list are available.
Loading source/semantic_spec_list NOT found. Check whether the source and semantic_spec_list are available.

"""

@@ -119,7 +139,7 @@ class BaseMarket:

def check_learnware(self, learnware: Learnware) -> bool:
"""Check the utility of a learnware
Parameters
----------
learnware : Learnware
@@ -132,7 +152,7 @@ class BaseMarket:
return True

def add_learnware(
self, learnware_name: str, model_path: str, stat_spec_path: str, property: dict, desc: str
self, learnware_name: str, model_path: str, stat_spec_path: str, semantic_spec: dict, desc: str
) -> Tuple[str, bool]:
"""Add a learnware into the market.

@@ -150,8 +170,8 @@ class BaseMarket:
stat_spec_path : str
Filepath for statistical specification, a '.npy' file.
How to pass parameters requires further discussion.
property : dict
property for new learnware, in dictionary format.
semantic_spec : dict
semantic_spec for new learnware, in dictionary format.
desc : str
Brief desciption for new learnware.

@@ -176,7 +196,7 @@ class BaseMarket:
Parameters
----------
user_info : BaseUserInfo
user_info with properties and statistical information
user_info with emantic specifications and statistical information

Returns
-------
@@ -186,28 +206,29 @@ class BaseMarket:
- first is recommended combination, None when no recommended combination is calculated or statistical specification is not provided.
- second is a list of matched learnwares
"""
def search_by_property():
def match_property(property1, property2):
if property1.keys() != property2.keys():
raise Exception("property key error".format(property1.keys(), property2.keys()))
for key in property1.keys():
if property1[key]['Type'] == 'Class':
if property1[key]['Values'] != property2[key]['Values']:

def search_by_semantic_spec():
def match_semantic_spec(semantic_spec1, semantic_spec2):
if semantic_spec1.keys() != semantic_spec2.keys():
raise Exception("semantic_spec key error".format(semantic_spec1.keys(), semantic_spec2.keys()))
for key in semantic_spec1.keys():
if semantic_spec1[key]["Type"] == "Class":
if semantic_spec1[key]["Values"] != semantic_spec2[key]["Values"]:
return False
elif property1[key]['Type'] == 'Tag':
if not (set(property1[key]['Values']) & set(property2[key]['Values'])):
elif semantic_spec1[key]["Type"] == "Tag":
if not (set(semantic_spec1[key]["Values"]) & set(semantic_spec2[key]["Values"])):
return False
return True
match_learnwares = []
for learnware in self.learnware_list:
learnware_property = learnware.get_specification().get_property()
user_property = user_info.get_property()
if match_property(learnware_property, user_property):
learnware_semantic_spec = learnware.get_specification().get_semantic_spec()
user_semantic_spec = user_info.get_semantic_spec()
if match_semantic_spec(learnware_semantic_spec, user_semantic_spec):
match_learnwares.append(learnware)
return match_learnwares
match_learnwares = search_by_property()
match_learnwares = search_by_semantic_spec()

pass

@@ -266,13 +287,13 @@ class BaseMarket:
"""
return True

def get_property_list(self) -> dict:
"""Return all properties available
def get_semantic_spec_list(self) -> dict:
"""Return all semantic specifications available

Returns
-------
dict
All properties in dictionary format
All emantic specifications in dictionary format

"""
return self.property_list
return self.semantic_spec_list

+ 1
- 1
learnware/market/evolve.py View File

@@ -6,7 +6,7 @@ from .anchor import AnchoredUserInfo, AnchoredMarket


class EvolvedMarket(AnchoredMarket):
"""Organize learnwares and enable them to continuously evolve
"""Organize learnwares and enable them to continuously evolve

Parameters
----------


+ 4
- 4
learnware/specification/base.py View File

@@ -16,15 +16,15 @@ class BaseStatSpecification:


class Specification:
def __init__(self, property=None):
self.property = property
def __init__(self, semantic_spec=None):
self.semantic_spec = semantic_spec
self.stat_spec = {} # stat_spec should be dict

def get_stat_spec(self):
return self.stat_spec

def get_property(self):
return self.property
def get_semantic_spec(self):
return self.semantic_spec

def update_stat_spec(self, name, new_stat_spec: BaseStatSpecification):
self.stat_spec[name] = new_stat_spec


+ 63
- 64
learnware/specification/rkme.py View File

@@ -12,19 +12,15 @@ from typing import Tuple, Any, List, Union, Dict
from .base import BaseStatSpecification


class RKMESpecification:
pass


def setup_seed(seed):
"""
Fix a random seed for addressing reproducibility issues.
Parameters
----------
seed : int
Random seed for torch, torch.cuda, numpy, random and cudnn libraries.
"""
Fix a random seed for addressing reproducibility issues.

Parameters
----------
seed : int
Random seed for torch, torch.cuda, numpy, random and cudnn libraries.
"""
torch.manual_seed(seed)
torch.cuda.manual_seed_all(seed)
np.random.seed(seed)
@@ -34,18 +30,18 @@ def setup_seed(seed):

def choose_device(cuda_idx=-1):
"""
Let users choose compuational device between CPU or GPU.
Parameters
----------
cuda_idx : int, optional
GPU index, by default -1 which stands for using CPU instead.
Returns
-------
torch.device
A torch.device object
"""
Let users choose compuational device between CPU or GPU.
Parameters
----------
cuda_idx : int, optional
GPU index, by default -1 which stands for using CPU instead.
Returns
-------
torch.device
A torch.device object
"""
if cuda_idx != -1:
device = torch.device(f"cuda:{cuda_idx}")
else:
@@ -55,47 +51,47 @@ def choose_device(cuda_idx=-1):

def torch_rbf_kernel(x1, x2, gamma) -> torch.Tensor:
"""
Use pytorch to compute rbf_kernel function at faster speed.
Parameters
----------
x1 : torch.Tensor
First vector in the rbf_kernel
x2 : torch.Tensor
Second vector in the rbf_kernel
gamma : float
Bandwidth in gaussian kernel
Returns
-------
torch.Tensor
The computed rbf_kernel value at x1, x2.
"""
Use pytorch to compute rbf_kernel function at faster speed.
Parameters
----------
x1 : torch.Tensor
First vector in the rbf_kernel
x2 : torch.Tensor
Second vector in the rbf_kernel
gamma : float
Bandwidth in gaussian kernel
Returns
-------
torch.Tensor
The computed rbf_kernel value at x1, x2.
"""
x1 = x1.double()
x2 = x2.double()
X12norm = torch.sum(x1 ** 2, 1, keepdim=True) - 2 * x1 @ x2.T + torch.sum(x2 ** 2, 1, keepdim=True).T
X12norm = torch.sum(x1**2, 1, keepdim=True) - 2 * x1 @ x2.T + torch.sum(x2**2, 1, keepdim=True).T
return torch.exp(-X12norm * gamma)


def solve_qp(K: np.ndarray, C: np.ndarray):
"""
Solver for the following quadratic programming(QP) problem:
- min 1/2 x^T K x - C^T x
s.t 1^T x - 1 = 0
- I x <= 0
Parameters
----------
K : np.ndarray
Parameter in the quadratic term.
C : np.ndarray
Parameter in the linear term.
Returns
-------
torch.tensor
Solution to the QP problem.
"""
Solver for the following quadratic programming(QP) problem:
- min 1/2 x^T K x - C^T x
s.t 1^T x - 1 = 0
- I x <= 0
Parameters
----------
K : np.ndarray
Parameter in the quadratic term.
C : np.ndarray
Parameter in the linear term.
Returns
-------
torch.tensor
Solution to the QP problem.
"""
n = K.shape[0]
P = matrix(K.cpu().numpy())
q = matrix(-C.cpu().numpy())
@@ -114,8 +110,7 @@ def solve_qp(K: np.ndarray, C: np.ndarray):


class RKMESpecification(BaseStatSpecification):
"""Reduced-set Kernel Mean Embedding (RKME) Specification
"""
"""Reduced-set Kernel Mean Embedding (RKME) Specification"""

def __init__(self, gamma: float = 0.1, cuda_idx: int = -1):
"""Initializing RKME parameters.
@@ -184,14 +179,14 @@ class RKMESpecification(BaseStatSpecification):
"""
alpha = None
self.num_points = X.shape[0]
# fill np.nan
X_nan = np.isnan(X)
if X_nan.max() == 1:
for col in range(X.shape[1]):
col_mean = np.nanmean(X[:, col])
X[:, col] = np.where(X_nan[:, col], col_mean, X[:, col])
if not reduce:
self.z = X
self.beta = 1 / self.num_points * np.ones(self.num_points)
@@ -296,14 +291,16 @@ class RKMESpecification(BaseStatSpecification):
Z = Z - step_size * grad_Z
self.z = Z

from .rkme import RKMESpecification

def inner_prod(self, Phi2: RKMESpecification) -> float:
"""Compute the inner product between two RKME specifications
Parameters
----------
Phi2 : RKMESpecification
The other RKME specification.
Returns
-------
float
@@ -354,7 +351,9 @@ class RKMESpecification(BaseStatSpecification):
rkme_to_save["beta"] = rkme_to_save["beta"].tolist()
rkme_to_save["device"] = "gpu" if rkme_to_save["cuda_idx"] != -1 else "cpu"
json.dump(
rkme_to_save, codecs.open(save_path, "w", encoding="utf-8"), separators=(",", ":"),
rkme_to_save,
codecs.open(save_path, "w", encoding="utf-8"),
separators=(",", ":"),
)

def load(self, filepath: str) -> bool:


+ 38
- 38
learnware/specification/utils.py View File

@@ -15,35 +15,35 @@ def generate_rkme_spec(
cuda_idx: int = -1,
) -> RKMESpecification:
"""
Interface for users to generate Reduced-set Kernel Mean Embedding (RKME) specification.
Return a RKMESpecification object, use .save() method to save as json file.
Interface for users to generate Reduced-set Kernel Mean Embedding (RKME) specification.
Return a RKMESpecification object, use .save() method to save as json file.


Parameters
----------
X : np.ndarray
Raw data in np.ndarray format.
Size of array: (n*d)
gamma : float
Bandwidth in gaussian kernel, by default 0.1.
K : int
Size of the construced reduced set.
step_size : float
Step size for gradient descent in the iterative optimization.
steps : int
Total rounds in the iterative optimization.
nonnegative_beta : bool, optional
True if weights for the reduced set are intended to be kept non-negative, by default False.
reduce : bool, optional
Whether shrink original data to a smaller set, by default True
cuda_idx : int
A flag indicating whether use CUDA during RKME computation. -1 indicates CUDA not used.
Parameters
----------
X : np.ndarray
Raw data in np.ndarray format.
Size of array: (n*d)
gamma : float
Bandwidth in gaussian kernel, by default 0.1.
K : int
Size of the construced reduced set.
step_size : float
Step size for gradient descent in the iterative optimization.
steps : int
Total rounds in the iterative optimization.
nonnegative_beta : bool, optional
True if weights for the reduced set are intended to be kept non-negative, by default False.
reduce : bool, optional
Whether shrink original data to a smaller set, by default True
cuda_idx : int
A flag indicating whether use CUDA during RKME computation. -1 indicates CUDA not used.

Returns
-------
RKMESpecification
A RKMESpecification object
"""
Returns
-------
RKMESpecification
A RKMESpecification object
"""
rkme_spec = RKMESpecification(gamma=gamma, cuda_idx=cuda_idx)
rkme_spec.generate_stat_spec_from_data(X, K, step_size, steps, nonnegative_beta, reduce)
return rkme_spec
@@ -51,19 +51,19 @@ def generate_rkme_spec(

def generate_stat_spec(X: np.ndarray) -> BaseStatSpecification:
"""
Interface for users to generate statistical specification.
Return a StatSpecification object, use .save() method to save as npy file.
Interface for users to generate statistical specification.
Return a StatSpecification object, use .save() method to save as npy file.


Parameters
----------
X : np.ndarray
Raw data in np.ndarray format.
Size of array: (n*d)
Parameters
----------
X : np.ndarray
Raw data in np.ndarray format.
Size of array: (n*d)

Returns
-------
StatSpecification
A StatSpecification object
"""
Returns
-------
StatSpecification
A StatSpecification object
"""
return None

Loading…
Cancel
Save